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.advance(); if !self.is_at_end() {
2692 let escaped = self.advance();
2693 match escaped {
2694 'n' => value.push('\n'),
2695 'r' => value.push('\r'),
2696 't' => value.push('\t'),
2697 '0' => value.push('\0'),
2698 'Z' => value.push('\x1A'), 'a' => value.push('\x07'), 'b' => value.push('\x08'), 'f' => value.push('\x0C'), 'v' => value.push('\x0B'), 'x' => {
2704 let mut hex = String::with_capacity(2);
2706 for _ in 0..2 {
2707 if !self.is_at_end() && self.peek().is_ascii_hexdigit() {
2708 hex.push(self.advance());
2709 }
2710 }
2711 if hex.len() == 2 {
2712 if let Ok(byte) = u8::from_str_radix(&hex, 16) {
2713 value.push(byte as char);
2714 } else {
2715 value.push('\\');
2716 value.push('x');
2717 value.push_str(&hex);
2718 }
2719 } else {
2720 value.push('\\');
2722 value.push('x');
2723 value.push_str(&hex);
2724 }
2725 }
2726 '\\' => value.push('\\'),
2727 '\'' => value.push('\''),
2728 '"' => value.push('"'),
2729 '%' => {
2730 value.push('%');
2732 }
2733 '_' => {
2734 value.push('_');
2736 }
2737 _ => {
2741 if !self.config.escape_follow_chars.is_empty() {
2742 value.push(escaped);
2744 } else {
2745 value.push('\\');
2747 value.push(escaped);
2748 }
2749 }
2750 }
2751 }
2752 } else {
2753 value.push(self.advance());
2754 }
2755 }
2756
2757 if self.is_at_end() {
2758 if self.config.recover_unterminated_string {
2759 self.add_token_with_text(TokenType::String, value);
2760 return Ok(());
2761 }
2762
2763 return Err(Error::tokenize(
2764 "Unterminated string",
2765 self.line,
2766 self.column,
2767 self.start,
2768 self.current,
2769 ));
2770 }
2771
2772 self.advance(); self.add_token_with_text(TokenType::String, value);
2774 Ok(())
2775 }
2776
2777 fn scan_double_quoted_string(&mut self) -> Result<()> {
2779 self.advance(); let mut value = String::new();
2781
2782 while !self.is_at_end() {
2783 let c = self.peek();
2784 if c == '"' {
2785 if self.peek_next() == '"' {
2786 value.push('"');
2788 self.advance();
2789 self.advance();
2790 } else {
2791 break;
2792 }
2793 } else if c == '\\' && self.config.string_escapes.contains(&'\\') {
2794 self.advance(); if !self.is_at_end() {
2797 let escaped = self.advance();
2798 match escaped {
2799 'n' => value.push('\n'),
2800 'r' => value.push('\r'),
2801 't' => value.push('\t'),
2802 '0' => value.push('\0'),
2803 'Z' => value.push('\x1A'), 'a' => value.push('\x07'), 'b' => value.push('\x08'), 'f' => value.push('\x0C'), 'v' => value.push('\x0B'), 'x' => {
2809 let mut hex = String::with_capacity(2);
2811 for _ in 0..2 {
2812 if !self.is_at_end() && self.peek().is_ascii_hexdigit() {
2813 hex.push(self.advance());
2814 }
2815 }
2816 if hex.len() == 2 {
2817 if let Ok(byte) = u8::from_str_radix(&hex, 16) {
2818 value.push(byte as char);
2819 } else {
2820 value.push('\\');
2821 value.push('x');
2822 value.push_str(&hex);
2823 }
2824 } else {
2825 value.push('\\');
2827 value.push('x');
2828 value.push_str(&hex);
2829 }
2830 }
2831 '\\' => value.push('\\'),
2832 '\'' => value.push('\''),
2833 '"' => value.push('"'),
2834 '%' => {
2835 value.push('%');
2837 }
2838 '_' => {
2839 value.push('_');
2841 }
2842 _ => {
2846 if !self.config.escape_follow_chars.is_empty() {
2847 value.push(escaped);
2849 } else {
2850 value.push('\\');
2852 value.push(escaped);
2853 }
2854 }
2855 }
2856 }
2857 } else {
2858 value.push(self.advance());
2859 }
2860 }
2861
2862 if self.is_at_end() {
2863 return Err(Error::tokenize(
2864 "Unterminated double-quoted string",
2865 self.line,
2866 self.column,
2867 self.start,
2868 self.current,
2869 ));
2870 }
2871
2872 self.advance(); self.add_token_with_text(TokenType::String, value);
2874 Ok(())
2875 }
2876
2877 fn scan_triple_quoted_string(&mut self, quote_char: char) -> Result<()> {
2878 self.advance();
2880 self.advance();
2881 self.advance();
2882 let mut value = String::new();
2883
2884 while !self.is_at_end() {
2885 if self.peek() == quote_char
2887 && self.current + 1 < self.size
2888 && self.char_at(self.current + 1) == quote_char
2889 && self.current + 2 < self.size
2890 && self.char_at(self.current + 2) == quote_char
2891 {
2892 break;
2894 }
2895 value.push(self.advance());
2896 }
2897
2898 if self.is_at_end() {
2899 return Err(Error::tokenize(
2900 "Unterminated triple-quoted string",
2901 self.line,
2902 self.column,
2903 self.start,
2904 self.current,
2905 ));
2906 }
2907
2908 self.advance();
2910 self.advance();
2911 self.advance();
2912 let token_type = if quote_char == '"' {
2913 TokenType::TripleDoubleQuotedString
2914 } else {
2915 TokenType::TripleSingleQuotedString
2916 };
2917 self.add_token_with_text(token_type, value);
2918 Ok(())
2919 }
2920
2921 fn scan_quoted_identifier(&mut self, end_quote: char) -> Result<()> {
2922 self.advance(); let mut value = String::new();
2924
2925 loop {
2926 if self.is_at_end() {
2927 return Err(Error::tokenize(
2928 "Unterminated identifier",
2929 self.line,
2930 self.column,
2931 self.start,
2932 self.current,
2933 ));
2934 }
2935 if end_quote == '`' && self.peek() == '\\' && self.peek_next() == end_quote {
2936 value.push(end_quote);
2938 self.advance(); self.advance(); continue;
2941 }
2942 if self.peek() == end_quote {
2943 if self.peek_next() == end_quote {
2944 value.push(end_quote);
2946 self.advance(); self.advance(); } else {
2949 break;
2951 }
2952 } else {
2953 value.push(self.peek());
2954 self.advance();
2955 }
2956 }
2957
2958 self.advance(); self.add_token_with_text(TokenType::QuotedIdentifier, value);
2960 Ok(())
2961 }
2962
2963 fn scan_unicode_quoted_string(&mut self, open_quote: char) -> Result<()> {
2968 self.advance(); let start = self.current;
2970 let close_quote = if open_quote == '\u{2018}' {
2972 '\u{2019}' } else {
2974 '\u{2019}' };
2976 while !self.is_at_end() && self.peek() != close_quote {
2977 self.advance();
2978 }
2979 let value = self.text_from_range(start, self.current);
2980 if !self.is_at_end() {
2981 self.advance(); }
2983 self.add_token_with_text(TokenType::String, value);
2984 Ok(())
2985 }
2986
2987 fn scan_unicode_quoted_identifier(&mut self, open_quote: char) -> Result<()> {
2990 self.advance(); let start = self.current;
2992 let close_quote = if open_quote == '\u{201C}' {
2993 '\u{201D}' } else {
2995 '\u{201D}' };
2997 while !self.is_at_end() && self.peek() != close_quote && self.peek() != '"' {
2998 self.advance();
2999 }
3000 let value = self.text_from_range(start, self.current);
3001 if !self.is_at_end() {
3002 self.advance(); }
3004 self.add_token_with_text(TokenType::QuotedIdentifier, value);
3005 Ok(())
3006 }
3007
3008 fn scan_number(&mut self) -> Result<()> {
3009 if self.config.hex_number_strings && self.peek() == '0' && !self.is_at_end() {
3011 let next = if self.current + 1 < self.size {
3012 self.char_at(self.current + 1)
3013 } else {
3014 '\0'
3015 };
3016 if next == 'x' || next == 'X' {
3017 self.advance();
3019 self.advance();
3020 let hex_start = self.current;
3022 if !self.advance_ascii_hex_digits() {
3023 while !self.is_at_end()
3024 && (self.peek().is_ascii_hexdigit() || self.peek() == '_')
3025 {
3026 if self.peek() == '_' && !self.peek_next().is_ascii_hexdigit() {
3027 break;
3028 }
3029 self.advance();
3030 }
3031 }
3032 if self.current > hex_start {
3033 let mut is_hex_float = false;
3035 if !self.is_at_end() && self.peek() == '.' {
3037 let after_dot = if self.current + 1 < self.size {
3038 self.char_at(self.current + 1)
3039 } else {
3040 '\0'
3041 };
3042 if after_dot.is_ascii_hexdigit() {
3043 is_hex_float = true;
3044 self.advance(); if !self.advance_ascii_hex_digits() {
3046 while !self.is_at_end() && self.peek().is_ascii_hexdigit() {
3047 self.advance();
3048 }
3049 }
3050 }
3051 }
3052 if !self.is_at_end() && (self.peek() == 'p' || self.peek() == 'P') {
3054 is_hex_float = true;
3055 self.advance(); if !self.is_at_end() && (self.peek() == '+' || self.peek() == '-') {
3057 self.advance();
3058 }
3059 if !self.advance_ascii_digits() {
3060 while !self.is_at_end() && self.peek().is_ascii_digit() {
3061 self.advance();
3062 }
3063 }
3064 }
3065 if is_hex_float {
3066 let raw_text = self.text_from_range(self.start, self.current);
3068 let full_text = if self.config.numbers_can_be_underscore_separated
3069 && raw_text.contains('_')
3070 {
3071 raw_text.replace('_', "")
3072 } else {
3073 raw_text
3074 };
3075 self.add_token_with_text(TokenType::Number, full_text);
3076 } else if self.config.hex_string_is_integer_type {
3077 let raw_value = self.text_from_range(hex_start, self.current);
3079 let hex_value = if self.config.numbers_can_be_underscore_separated
3080 && raw_value.contains('_')
3081 {
3082 raw_value.replace('_', "")
3083 } else {
3084 raw_value
3085 };
3086 self.add_token_with_text(TokenType::HexNumber, hex_value);
3087 } else {
3088 let raw_value = self.text_from_range(hex_start, self.current);
3090 let hex_value = if self.config.numbers_can_be_underscore_separated
3091 && raw_value.contains('_')
3092 {
3093 raw_value.replace('_', "")
3094 } else {
3095 raw_value
3096 };
3097 self.add_token_with_text(TokenType::HexString, hex_value);
3098 }
3099 return Ok(());
3100 }
3101 self.current = self.start + 1;
3104 }
3105 }
3106
3107 if !self.advance_ascii_digits() {
3109 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3110 if self.peek() == '_' && (self.is_at_end() || !self.peek_next().is_ascii_digit()) {
3112 break;
3113 }
3114 self.advance();
3115 }
3116 }
3117
3118 if self.peek() == '.' {
3122 let next = self.peek_next();
3123 if next != '.' {
3129 self.advance(); if !self.advance_ascii_digits() {
3132 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_')
3133 {
3134 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3135 break;
3136 }
3137 self.advance();
3138 }
3139 }
3140 }
3141 }
3142
3143 if self.peek() == 'e' || self.peek() == 'E' {
3145 self.advance();
3146 if self.peek() == '+' || self.peek() == '-' {
3147 self.advance();
3148 }
3149 if !self.advance_ascii_digits() {
3150 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3151 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3152 break;
3153 }
3154 self.advance();
3155 }
3156 }
3157 }
3158
3159 let source_text = self
3160 .cursor
3161 .source_range(self.source, self.start, self.current);
3162 let raw_owned = source_text
3163 .is_none()
3164 .then(|| self.text_from_range(self.start, self.current));
3165 let raw_text = source_text.unwrap_or_else(|| {
3166 raw_owned
3167 .as_deref()
3168 .expect("non-ASCII numbers own their text")
3169 });
3170 let normalized = (self.config.numbers_can_be_underscore_separated
3173 && raw_text.contains('_'))
3174 .then(|| raw_text.replace('_', ""));
3175 let text = normalized.as_deref().unwrap_or(raw_text);
3176
3177 if !self.config.numeric_literals.is_empty() && !self.is_at_end() {
3179 let next_char: String = self.peek().to_ascii_uppercase().to_string();
3180 let suffix_match = if self.current + 1 < self.size {
3182 let two_char: String = [
3183 self.char_at(self.current).to_ascii_uppercase(),
3184 self.char_at(self.current + 1).to_ascii_uppercase(),
3185 ]
3186 .iter()
3187 .collect();
3188 if self.config.numeric_literals.contains_key(&two_char) {
3189 let after_suffix = if self.current + 2 < self.size {
3191 self.char_at(self.current + 2)
3192 } else {
3193 ' '
3194 };
3195 if !after_suffix.is_alphanumeric() && after_suffix != '_' {
3196 Some((two_char, 2))
3197 } else {
3198 None
3199 }
3200 } else if self.config.numeric_literals.contains_key(&next_char) {
3201 let after_suffix = if self.current + 1 < self.size {
3203 self.char_at(self.current + 1)
3204 } else {
3205 ' '
3206 };
3207 if !after_suffix.is_alphanumeric() && after_suffix != '_' {
3208 Some((next_char, 1))
3209 } else {
3210 None
3211 }
3212 } else {
3213 None
3214 }
3215 } else if self.config.numeric_literals.contains_key(&next_char) {
3216 Some((next_char, 1))
3218 } else {
3219 None
3220 };
3221
3222 if let Some((suffix, len)) = suffix_match {
3223 for _ in 0..len {
3225 self.advance();
3226 }
3227 let type_name = self
3230 .config
3231 .numeric_literals
3232 .get(&suffix)
3233 .expect("suffix verified by contains_key above")
3234 .clone();
3235 let combined = format!("{}::{}", text, type_name);
3236 self.add_token_with_text(TokenType::Number, combined);
3237 return Ok(());
3238 }
3239 }
3240
3241 if self.config.identifiers_can_start_with_digit && !self.is_at_end() {
3244 let next = self.peek();
3245 if next.is_alphabetic() || next == '_' {
3246 if !self.advance_ascii_identifier() {
3248 while !self.is_at_end() {
3249 let ch = self.peek();
3250 if ch.is_alphanumeric() || ch == '_' {
3251 self.advance();
3252 } else {
3253 break;
3254 }
3255 }
3256 }
3257 self.add_token(TokenType::Identifier);
3258 return Ok(());
3259 }
3260 }
3261
3262 if let Some(text) = normalized.or(raw_owned) {
3263 self.add_token_with_text(TokenType::Number, text);
3264 } else {
3265 self.add_token(TokenType::Number);
3266 }
3267 Ok(())
3268 }
3269
3270 fn scan_number_starting_with_dot(&mut self) -> Result<()> {
3272 self.advance();
3274
3275 if !self.advance_ascii_digits() {
3277 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3278 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3279 break;
3280 }
3281 self.advance();
3282 }
3283 }
3284
3285 if self.peek() == 'e' || self.peek() == 'E' {
3287 self.advance();
3288 if self.peek() == '+' || self.peek() == '-' {
3289 self.advance();
3290 }
3291 if !self.advance_ascii_digits() {
3292 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3293 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3294 break;
3295 }
3296 self.advance();
3297 }
3298 }
3299 }
3300
3301 let source_text = self
3302 .cursor
3303 .source_range(self.source, self.start, self.current);
3304 let raw_owned = source_text
3305 .is_none()
3306 .then(|| self.text_from_range(self.start, self.current));
3307 let raw_text = source_text.unwrap_or_else(|| {
3308 raw_owned
3309 .as_deref()
3310 .expect("non-ASCII numbers own their text")
3311 });
3312 let normalized = (self.config.numbers_can_be_underscore_separated
3315 && raw_text.contains('_'))
3316 .then(|| raw_text.replace('_', ""));
3317 if let Some(text) = normalized.or(raw_owned) {
3318 self.add_token_with_text(TokenType::Number, text);
3319 } else {
3320 self.add_token(TokenType::Number);
3321 }
3322 Ok(())
3323 }
3324
3325 #[inline]
3328 fn lookup_keyword_ascii(keywords: &HashMap<String, TokenType>, text: &str) -> TokenType {
3329 if text.len() > 128 {
3330 return TokenType::Var;
3331 }
3332 let mut buf = [0u8; 128];
3333 for (i, b) in text.bytes().enumerate() {
3334 buf[i] = b.to_ascii_uppercase();
3335 }
3336 if let Ok(upper) = std::str::from_utf8(&buf[..text.len()]) {
3337 keywords.get(upper).copied().unwrap_or(TokenType::Var)
3338 } else {
3339 TokenType::Var
3340 }
3341 }
3342
3343 fn scan_identifier_or_keyword(&mut self) -> Result<()> {
3344 let first_char = self.peek();
3346 if !first_char.is_alphanumeric() && first_char != '_' {
3347 let c = self.advance();
3349 return Err(Error::tokenize(
3350 format!("Unexpected character: '{}'", c),
3351 self.line,
3352 self.column,
3353 self.start,
3354 self.current,
3355 ));
3356 }
3357
3358 if !self.advance_ascii_identifier() {
3359 while !self.is_at_end() {
3360 let c = self.peek();
3361 if c == '#' {
3365 let next_c = if self.current + 1 < self.size {
3366 self.char_at(self.current + 1)
3367 } else {
3368 '\0'
3369 };
3370 if next_c == '>' || next_c == '-' {
3371 break; }
3373 self.advance();
3374 } else if c.is_alphanumeric() || c == '_' || c == '$' || c == '@' {
3375 self.advance();
3376 } else {
3377 break;
3378 }
3379 }
3380 }
3381
3382 let source_text = self
3383 .cursor
3384 .source_range(self.source, self.start, self.current);
3385 let owned_text = source_text
3386 .is_none()
3387 .then(|| self.text_from_range(self.start, self.current));
3388 let text = source_text.unwrap_or_else(|| {
3389 owned_text
3390 .as_deref()
3391 .expect("non-ASCII identifiers own their text")
3392 });
3393
3394 if text.eq_ignore_ascii_case("NOT") && self.peek() == '=' {
3396 self.advance(); self.add_token(TokenType::Neq);
3398 return Ok(());
3399 }
3400
3401 let next_char = self.peek();
3404 let is_single_quote = next_char == '\'';
3405 let is_double_quote = next_char == '"' && self.config.quotes.contains_key("\"");
3406 let is_double_quote_for_raw = next_char == '"';
3409
3410 if text.eq_ignore_ascii_case("R") && (is_single_quote || is_double_quote_for_raw) {
3413 let quote_char = if is_single_quote { '\'' } else { '"' };
3416 self.advance(); if self.peek() == quote_char && self.peek_next() == quote_char {
3420 self.advance(); self.advance(); let string_value = self.scan_raw_triple_quoted_content(quote_char)?;
3424 self.add_token_with_text(TokenType::RawString, string_value);
3425 } else {
3426 let string_value = self.scan_raw_string_content(quote_char)?;
3427 self.add_token_with_text(TokenType::RawString, string_value);
3428 }
3429 return Ok(());
3430 }
3431
3432 if is_single_quote || is_double_quote {
3433 if text.eq_ignore_ascii_case("N") {
3434 self.advance(); let string_value = if is_single_quote {
3437 self.scan_string_content()?
3438 } else {
3439 self.scan_double_quoted_string_content()?
3440 };
3441 self.add_token_with_text(TokenType::NationalString, string_value);
3442 return Ok(());
3443 } else if text.eq_ignore_ascii_case("E") {
3444 let lowercase = text == "e";
3448 let prefix = if lowercase { "e:" } else { "E:" };
3449 self.advance(); let string_value = self.scan_string_content_with_escapes(true)?;
3451 self.add_token_with_text(
3452 TokenType::EscapeString,
3453 format!("{}{}", prefix, string_value),
3454 );
3455 return Ok(());
3456 } else if text.eq_ignore_ascii_case("X") {
3457 self.advance(); let string_value = if is_single_quote {
3460 self.scan_string_content()?
3461 } else {
3462 self.scan_double_quoted_string_content()?
3463 };
3464 self.add_token_with_text(TokenType::HexString, string_value);
3465 return Ok(());
3466 } else if text.eq_ignore_ascii_case("B") && is_double_quote {
3467 self.advance(); let string_value = self.scan_double_quoted_string_content()?;
3470 self.add_token_with_text(TokenType::ByteString, string_value);
3471 return Ok(());
3472 } else if text.eq_ignore_ascii_case("B") && is_single_quote {
3473 self.advance(); let string_value = self.scan_string_content()?;
3477 if self.config.b_prefix_is_byte_string {
3478 self.add_token_with_text(TokenType::ByteString, string_value);
3479 } else {
3480 self.add_token_with_text(TokenType::BitString, string_value);
3481 }
3482 return Ok(());
3483 }
3484 }
3485
3486 if text.eq_ignore_ascii_case("U")
3488 && self.peek() == '&'
3489 && self.current + 1 < self.size
3490 && self.char_at(self.current + 1) == '\''
3491 {
3492 self.advance(); self.advance(); let string_value = self.scan_string_content()?;
3495 self.add_token_with_text(TokenType::UnicodeString, string_value);
3496 return Ok(());
3497 }
3498
3499 let token_type = Self::lookup_keyword_ascii(&self.config.keywords, &text);
3500
3501 if let Some(text) = owned_text {
3502 self.add_token_with_text(token_type, text);
3503 } else {
3504 self.add_token_from_source(token_type, self.start, self.current);
3505 }
3506 Ok(())
3507 }
3508
3509 fn scan_string_content_with_escapes(
3513 &mut self,
3514 force_backslash_escapes: bool,
3515 ) -> Result<String> {
3516 let use_backslash_escapes =
3517 force_backslash_escapes || self.config.string_escapes.contains(&'\\');
3518 if let Some((start, end)) = self.try_scan_simple_quoted_content('\'', use_backslash_escapes)
3519 {
3520 return Ok(self.text_from_range(start, end));
3521 }
3522 let mut value = String::new();
3523
3524 while !self.is_at_end() {
3525 let c = self.peek();
3526 if c == '\'' {
3527 if self.peek_next() == '\'' {
3528 value.push('\'');
3530 self.advance();
3531 self.advance();
3532 } else {
3533 break;
3534 }
3535 } else if c == '\\' && use_backslash_escapes {
3536 value.push(self.advance());
3538 if !self.is_at_end() {
3539 value.push(self.advance());
3540 }
3541 } else {
3542 value.push(self.advance());
3543 }
3544 }
3545
3546 if self.is_at_end() {
3547 return Err(Error::tokenize(
3548 "Unterminated string",
3549 self.line,
3550 self.column,
3551 self.start,
3552 self.current,
3553 ));
3554 }
3555
3556 self.advance(); Ok(value)
3558 }
3559
3560 fn scan_string_content(&mut self) -> Result<String> {
3562 self.scan_string_content_with_escapes(false)
3563 }
3564
3565 fn scan_double_quoted_string_content(&mut self) -> Result<String> {
3568 let use_backslash_escapes = self.config.string_escapes.contains(&'\\');
3569 if let Some((start, end)) = self.try_scan_simple_quoted_content('"', use_backslash_escapes)
3570 {
3571 return Ok(self.text_from_range(start, end));
3572 }
3573 let mut value = String::new();
3574
3575 while !self.is_at_end() {
3576 let c = self.peek();
3577 if c == '"' {
3578 if self.peek_next() == '"' {
3579 value.push('"');
3581 self.advance();
3582 self.advance();
3583 } else {
3584 break;
3585 }
3586 } else if c == '\\' && use_backslash_escapes {
3587 self.advance(); if !self.is_at_end() {
3590 let escaped = self.advance();
3591 match escaped {
3592 'n' => value.push('\n'),
3593 'r' => value.push('\r'),
3594 't' => value.push('\t'),
3595 '0' => value.push('\0'),
3596 '\\' => value.push('\\'),
3597 '"' => value.push('"'),
3598 '\'' => value.push('\''),
3599 'x' => {
3600 let mut hex = String::new();
3602 for _ in 0..2 {
3603 if !self.is_at_end() && self.peek().is_ascii_hexdigit() {
3604 hex.push(self.advance());
3605 }
3606 }
3607 if let Ok(byte) = u8::from_str_radix(&hex, 16) {
3608 value.push(byte as char);
3609 } else {
3610 value.push('\\');
3612 value.push('x');
3613 value.push_str(&hex);
3614 }
3615 }
3616 _ => {
3617 value.push('\\');
3619 value.push(escaped);
3620 }
3621 }
3622 }
3623 } else {
3624 value.push(self.advance());
3625 }
3626 }
3627
3628 if self.is_at_end() {
3629 return Err(Error::tokenize(
3630 "Unterminated double-quoted string",
3631 self.line,
3632 self.column,
3633 self.start,
3634 self.current,
3635 ));
3636 }
3637
3638 self.advance(); Ok(value)
3640 }
3641
3642 fn scan_raw_string_content(&mut self, quote_char: char) -> Result<String> {
3647 if let Some((start, end)) = self.try_scan_simple_quoted_content(
3648 quote_char,
3649 self.config.string_escapes_allowed_in_raw_strings,
3650 ) {
3651 return Ok(self.text_from_range(start, end));
3652 }
3653 let mut value = String::new();
3654
3655 while !self.is_at_end() {
3656 let c = self.peek();
3657 if c == quote_char {
3658 if self.peek_next() == quote_char {
3659 value.push(quote_char);
3661 self.advance();
3662 self.advance();
3663 } else {
3664 break;
3665 }
3666 } else if c == '\\'
3667 && self.peek_next() == quote_char
3668 && self.config.string_escapes_allowed_in_raw_strings
3669 {
3670 value.push(quote_char);
3674 self.advance(); self.advance(); } else {
3677 value.push(self.advance());
3679 }
3680 }
3681
3682 if self.is_at_end() {
3683 return Err(Error::tokenize(
3684 "Unterminated raw string",
3685 self.line,
3686 self.column,
3687 self.start,
3688 self.current,
3689 ));
3690 }
3691
3692 self.advance(); Ok(value)
3694 }
3695
3696 fn scan_raw_triple_quoted_content(&mut self, quote_char: char) -> Result<String> {
3699 let mut value = String::new();
3700
3701 while !self.is_at_end() {
3702 let c = self.peek();
3703 if c == quote_char && self.peek_next() == quote_char {
3704 if self.current + 2 < self.size && self.char_at(self.current + 2) == quote_char {
3706 self.advance(); self.advance(); self.advance(); return Ok(value);
3711 }
3712 }
3713 let ch = self.advance();
3715 value.push(ch);
3716 }
3717
3718 Err(Error::tokenize(
3719 "Unterminated raw triple-quoted string",
3720 self.line,
3721 self.column,
3722 self.start,
3723 self.current,
3724 ))
3725 }
3726
3727 fn scan_dollar_identifier(&mut self) -> Result<()> {
3732 self.advance();
3734
3735 while !self.is_at_end() {
3737 let c = self.peek();
3738 if c.is_alphanumeric() || c == '_' || c == '$' {
3739 self.advance();
3740 } else {
3741 break;
3742 }
3743 }
3744
3745 self.add_token(TokenType::Var);
3746 Ok(())
3747 }
3748
3749 fn scan_tsql_identifier(&mut self) -> Result<()> {
3750 let first = self.advance();
3752
3753 if first == '#' && self.peek() == '#' {
3755 self.advance();
3756 }
3757
3758 if !self.advance_ascii_identifier() {
3760 while !self.is_at_end() {
3761 let c = self.peek();
3762 if c.is_alphanumeric() || c == '_' || c == '$' || c == '#' || c == '@' {
3763 self.advance();
3764 } else {
3765 break;
3766 }
3767 }
3768 }
3769
3770 self.add_token(TokenType::Var);
3772 Ok(())
3773 }
3774
3775 fn try_scan_insert_format_raw_data(&mut self) -> Option<String> {
3779 let len = self.tokens.len();
3780 if len < 3 {
3781 return None;
3782 }
3783
3784 let last = &self.tokens[len - 1];
3786 if last.text(self.source).eq_ignore_ascii_case("VALUES") {
3787 return None;
3788 }
3789 if !matches!(last.token_type(), TokenType::Var | TokenType::Identifier) {
3790 return None;
3791 }
3792
3793 let format_tok = &self.tokens[len - 2];
3795 if !format_tok.text(self.source).eq_ignore_ascii_case("FORMAT") {
3796 return None;
3797 }
3798
3799 let has_insert = self.tokens[..len - 2]
3801 .iter()
3802 .rev()
3803 .take(20)
3804 .any(|t| t.token_type() == TokenType::Insert);
3805 if !has_insert {
3806 return None;
3807 }
3808
3809 let raw_start = self.current;
3813 while !self.is_at_end() {
3814 let c = self.peek();
3815 if c == '\n' {
3816 let saved = self.current;
3818 self.advance(); while !self.is_at_end() && self.peek() == '\r' {
3821 self.advance();
3822 }
3823 if self.is_at_end() || self.peek() == '\n' {
3824 let raw = self.text_from_range(raw_start, saved);
3827 return Some(raw.trim().to_string());
3828 }
3829 } else {
3831 self.advance();
3832 }
3833 }
3834
3835 let raw = self.text_from_range(raw_start, self.current);
3837 let trimmed = raw.trim().to_string();
3838 if trimmed.is_empty() {
3839 None
3840 } else {
3841 Some(trimmed)
3842 }
3843 }
3844
3845 fn add_token(&mut self, token_type: TokenType) {
3846 self.add_token_from_source(token_type, self.start, self.current);
3847 }
3848
3849 fn add_token_from_source(&mut self, token_type: TokenType, text_start: usize, text_end: usize) {
3850 let span = Span::new(self.start, self.current, self.line, self.column);
3851 if let Some(stats) = &mut self.guard_stats {
3852 stats.observe(token_type, span);
3853 }
3854 let mut token = if self
3855 .cursor
3856 .source_range(self.source, text_start, text_end)
3857 .is_some()
3858 {
3859 T::from_source(
3860 token_type,
3861 self.source,
3862 text_start,
3863 text_end,
3864 span,
3865 self.shared_source.as_ref(),
3866 )
3867 } else {
3868 T::from_owned(
3869 token_type,
3870 self.cursor
3871 .text_from_range(self.source, text_start, text_end),
3872 span,
3873 )
3874 };
3875 token.comments_mut().append(&mut self.comments);
3876 self.tokens.push(token);
3877 }
3878
3879 fn add_token_with_text(&mut self, token_type: TokenType, text: String) {
3880 let span = Span::new(self.start, self.current, self.line, self.column);
3881 if let Some(stats) = &mut self.guard_stats {
3882 stats.observe(token_type, span);
3883 }
3884 let mut token = T::from_owned(token_type, text, span);
3885 token.comments_mut().append(&mut self.comments);
3886 self.tokens.push(token);
3887 }
3888}
3889
3890#[cfg(test)]
3891mod tests {
3892 use super::*;
3893
3894 #[test]
3895 fn ascii_fast_path_matches_character_buffer_path() {
3896 let tokenizer = Tokenizer::default();
3897 let inputs = [
3898 "SELECT a, b FROM t WHERE id IN (1, 2, 3)",
3899 "SELECT 'it''s', \"quoted\", $1 /* comment */ FROM schema.table",
3900 "INSERT INTO t VALUES (1, 'a'), (2, 'b'); UPDATE t SET value = 'c'",
3901 "SELECT $$body$$, $tag$content$tag$, 0xFF, 1.25e-2",
3902 ];
3903
3904 for sql in inputs {
3905 assert_eq!(
3906 tokenizer.tokenize(sql).unwrap(),
3907 tokenizer.tokenize_without_ascii_fast_path(sql).unwrap(),
3908 "tokenization differs for {sql}"
3909 );
3910 }
3911 }
3912
3913 #[test]
3914 fn parser_tokens_match_public_tokens() {
3915 let tokenizer = Tokenizer::default();
3916 let inputs = [
3917 "SELECT alpha, 123, 'plain' FROM schema.table WHERE id = 42",
3918 "SELECT 'it''s', $$body$$, $tag$content$tag$ /* comment */",
3919 "SELECT cafe, 'naive' FROM t\nWHERE value >= 1.25e-2",
3920 "SELECT cafe, 'caf\u{e9}', \u{3b4}elta FROM donn\u{e9}es",
3921 ];
3922
3923 for sql in inputs {
3924 let public = tokenizer.tokenize(sql).unwrap();
3925 let source: Arc<str> = Arc::from(sql);
3926 let (parser, stats) = tokenizer.tokenize_for_parser(&source).unwrap();
3927 let materialized = parser
3928 .iter()
3929 .map(|token| Token {
3930 token_type: token.token_type,
3931 text: token.text_owned(),
3932 span: token.span,
3933 comments: token.comments.clone(),
3934 trailing_comments: token.trailing_comments.clone(),
3935 })
3936 .collect::<Vec<_>>();
3937
3938 assert_eq!(
3939 materialized, public,
3940 "parser tokenization differs for {sql}"
3941 );
3942 assert_eq!(stats.token_count, public.len());
3943 }
3944 }
3945
3946 #[test]
3947 fn parser_tokens_borrow_unchanged_ascii_text() {
3948 let tokenizer = Tokenizer::default();
3949 let source: Arc<str> = Arc::from("SELECT alpha, 123, 'plain'");
3950 let (tokens, _) = tokenizer.tokenize_for_parser(&source).unwrap();
3951
3952 assert!(tokens
3953 .iter()
3954 .all(|token| matches!(&token.text, ParserTokenText::Source { .. })));
3955 }
3956
3957 #[test]
3958 fn test_simple_select() {
3959 let tokenizer = Tokenizer::default();
3960 let tokens = tokenizer.tokenize("SELECT 1").unwrap();
3961
3962 assert_eq!(tokens.len(), 2);
3963 assert_eq!(tokens[0].token_type, TokenType::Select);
3964 assert_eq!(tokens[1].token_type, TokenType::Number);
3965 assert_eq!(tokens[1].text, "1");
3966 }
3967
3968 #[test]
3969 fn test_select_with_identifier() {
3970 let tokenizer = Tokenizer::default();
3971 let tokens = tokenizer.tokenize("SELECT a, b FROM t").unwrap();
3972
3973 assert_eq!(tokens.len(), 6);
3974 assert_eq!(tokens[0].token_type, TokenType::Select);
3975 assert_eq!(tokens[1].token_type, TokenType::Var);
3976 assert_eq!(tokens[1].text, "a");
3977 assert_eq!(tokens[2].token_type, TokenType::Comma);
3978 assert_eq!(tokens[3].token_type, TokenType::Var);
3979 assert_eq!(tokens[3].text, "b");
3980 assert_eq!(tokens[4].token_type, TokenType::From);
3981 assert_eq!(tokens[5].token_type, TokenType::Var);
3982 assert_eq!(tokens[5].text, "t");
3983 }
3984
3985 #[test]
3986 fn test_string_literal() {
3987 let tokenizer = Tokenizer::default();
3988 let tokens = tokenizer.tokenize("SELECT 'hello'").unwrap();
3989
3990 assert_eq!(tokens.len(), 2);
3991 assert_eq!(tokens[1].token_type, TokenType::String);
3992 assert_eq!(tokens[1].text, "hello");
3993 }
3994
3995 #[test]
3996 fn test_escaped_string() {
3997 let tokenizer = Tokenizer::default();
3998 let tokens = tokenizer.tokenize("SELECT 'it''s'").unwrap();
3999
4000 assert_eq!(tokens.len(), 2);
4001 assert_eq!(tokens[1].token_type, TokenType::String);
4002 assert_eq!(tokens[1].text, "it's");
4003 }
4004
4005 #[test]
4006 fn test_terminal_backslash_quote_recovery() {
4007 let mut config = TokenizerConfig::default();
4008 config.string_escapes.push('\\');
4009 config.recover_terminal_backslash_quote = true;
4010 let tokenizer = Tokenizer::new(config);
4011 let tokens = tokenizer
4012 .tokenize("SHOW FUNCTIONS LIKE 'a\\' OR 1=1")
4013 .unwrap();
4014
4015 assert_eq!(tokens.len(), 8);
4016 assert_eq!(tokens[3].token_type, TokenType::String);
4017 assert_eq!(tokens[3].text, "a\\");
4018 assert_eq!(tokens[4].token_type, TokenType::Or);
4019 }
4020
4021 #[test]
4022 fn test_comments() {
4023 let tokenizer = Tokenizer::default();
4024 let tokens = tokenizer.tokenize("SELECT -- comment\n1").unwrap();
4025
4026 assert_eq!(tokens.len(), 2);
4027 assert_eq!(tokens[0].trailing_comments.len(), 1);
4030 assert_eq!(tokens[0].trailing_comments[0], " comment");
4031 }
4032
4033 #[test]
4034 fn test_comment_in_and_chain() {
4035 use crate::generator::Generator;
4036 use crate::parser::Parser;
4037
4038 let sql = "SELECT a FROM b WHERE foo\n-- c1\nAND bar\n-- c2\nAND bla";
4040 let ast = Parser::parse_sql(sql).unwrap();
4041 let mut gen = Generator::default();
4042 let output = gen.generate(&ast[0]).unwrap();
4043 assert_eq!(
4044 output,
4045 "SELECT a FROM b WHERE foo AND /* c1 */ bar AND /* c2 */ bla"
4046 );
4047 }
4048
4049 #[test]
4050 fn test_operators() {
4051 let tokenizer = Tokenizer::default();
4052 let tokens = tokenizer.tokenize("1 + 2 * 3").unwrap();
4053
4054 assert_eq!(tokens.len(), 5);
4055 assert_eq!(tokens[0].token_type, TokenType::Number);
4056 assert_eq!(tokens[1].token_type, TokenType::Plus);
4057 assert_eq!(tokens[2].token_type, TokenType::Number);
4058 assert_eq!(tokens[3].token_type, TokenType::Star);
4059 assert_eq!(tokens[4].token_type, TokenType::Number);
4060 }
4061
4062 #[test]
4063 fn test_comparison_operators() {
4064 let tokenizer = Tokenizer::default();
4065 let tokens = tokenizer.tokenize("a <= b >= c != d").unwrap();
4066
4067 assert_eq!(tokens[1].token_type, TokenType::Lte);
4068 assert_eq!(tokens[3].token_type, TokenType::Gte);
4069 assert_eq!(tokens[5].token_type, TokenType::Neq);
4070 }
4071
4072 #[test]
4073 fn test_national_string() {
4074 let tokenizer = Tokenizer::default();
4075 let tokens = tokenizer.tokenize("N'abc'").unwrap();
4076
4077 assert_eq!(
4078 tokens.len(),
4079 1,
4080 "Expected 1 token for N'abc', got {:?}",
4081 tokens
4082 );
4083 assert_eq!(tokens[0].token_type, TokenType::NationalString);
4084 assert_eq!(tokens[0].text, "abc");
4085 }
4086
4087 #[test]
4088 fn test_hex_string() {
4089 let tokenizer = Tokenizer::default();
4090 let tokens = tokenizer.tokenize("X'ABCD'").unwrap();
4091
4092 assert_eq!(
4093 tokens.len(),
4094 1,
4095 "Expected 1 token for X'ABCD', got {:?}",
4096 tokens
4097 );
4098 assert_eq!(tokens[0].token_type, TokenType::HexString);
4099 assert_eq!(tokens[0].text, "ABCD");
4100 }
4101
4102 #[test]
4103 fn test_bit_string() {
4104 let tokenizer = Tokenizer::default();
4105 let tokens = tokenizer.tokenize("B'01010'").unwrap();
4106
4107 assert_eq!(
4108 tokens.len(),
4109 1,
4110 "Expected 1 token for B'01010', got {:?}",
4111 tokens
4112 );
4113 assert_eq!(tokens[0].token_type, TokenType::BitString);
4114 assert_eq!(tokens[0].text, "01010");
4115 }
4116
4117 #[test]
4118 fn test_trailing_dot_number() {
4119 let tokenizer = Tokenizer::default();
4120
4121 let tokens = tokenizer.tokenize("SELECT 1.").unwrap();
4123 assert_eq!(
4124 tokens.len(),
4125 2,
4126 "Expected 2 tokens for 'SELECT 1.', got {:?}",
4127 tokens
4128 );
4129 assert_eq!(tokens[1].token_type, TokenType::Number);
4130 assert_eq!(tokens[1].text, "1.");
4131
4132 let tokens = tokenizer.tokenize("SELECT 1.5").unwrap();
4134 assert_eq!(tokens[1].text, "1.5");
4135
4136 let tokens = tokenizer.tokenize("SELECT 1.a").unwrap();
4139 assert_eq!(
4140 tokens.len(),
4141 3,
4142 "Expected 3 tokens for 'SELECT 1.a', got {:?}",
4143 tokens
4144 );
4145 assert_eq!(tokens[1].token_type, TokenType::Number);
4146 assert_eq!(tokens[1].text, "1.");
4147 assert_eq!(tokens[2].token_type, TokenType::Var);
4148
4149 let tokens = tokenizer.tokenize("SELECT 1..2").unwrap();
4151 assert_eq!(tokens[1].token_type, TokenType::Number);
4152 assert_eq!(tokens[1].text, "1");
4153 assert_eq!(tokens[2].token_type, TokenType::Dot);
4154 assert_eq!(tokens[3].token_type, TokenType::Dot);
4155 assert_eq!(tokens[4].token_type, TokenType::Number);
4156 assert_eq!(tokens[4].text, "2");
4157 }
4158
4159 #[test]
4160 fn test_leading_dot_number() {
4161 let tokenizer = Tokenizer::default();
4162
4163 let tokens = tokenizer.tokenize(".25").unwrap();
4165 assert_eq!(
4166 tokens.len(),
4167 1,
4168 "Expected 1 token for '.25', got {:?}",
4169 tokens
4170 );
4171 assert_eq!(tokens[0].token_type, TokenType::Number);
4172 assert_eq!(tokens[0].text, ".25");
4173
4174 let tokens = tokenizer.tokenize("SAMPLE (.25)").unwrap();
4176 assert_eq!(
4177 tokens.len(),
4178 4,
4179 "Expected 4 tokens for 'SAMPLE (.25)', got {:?}",
4180 tokens
4181 );
4182 assert_eq!(tokens[0].token_type, TokenType::Sample);
4183 assert_eq!(tokens[1].token_type, TokenType::LParen);
4184 assert_eq!(tokens[2].token_type, TokenType::Number);
4185 assert_eq!(tokens[2].text, ".25");
4186 assert_eq!(tokens[3].token_type, TokenType::RParen);
4187
4188 let tokens = tokenizer.tokenize(".5e10").unwrap();
4190 assert_eq!(
4191 tokens.len(),
4192 1,
4193 "Expected 1 token for '.5e10', got {:?}",
4194 tokens
4195 );
4196 assert_eq!(tokens[0].token_type, TokenType::Number);
4197 assert_eq!(tokens[0].text, ".5e10");
4198
4199 let tokens = tokenizer.tokenize("a.b").unwrap();
4201 assert_eq!(
4202 tokens.len(),
4203 3,
4204 "Expected 3 tokens for 'a.b', got {:?}",
4205 tokens
4206 );
4207 assert_eq!(tokens[1].token_type, TokenType::Dot);
4208 }
4209
4210 #[test]
4211 fn test_unrecognized_character() {
4212 let tokenizer = Tokenizer::default();
4213
4214 let result = tokenizer.tokenize("SELECT \u{2018}hello\u{2019}");
4216 assert!(
4217 result.is_ok(),
4218 "Curly quotes should be tokenized as strings"
4219 );
4220
4221 let result = tokenizer.tokenize("SELECT • FROM t");
4223 assert!(result.is_err());
4224 }
4225
4226 #[test]
4227 fn test_colon_eq_tokenization() {
4228 let tokenizer = Tokenizer::default();
4229
4230 let tokens = tokenizer.tokenize("a := 1").unwrap();
4232 assert_eq!(tokens.len(), 3);
4233 assert_eq!(tokens[0].token_type, TokenType::Var);
4234 assert_eq!(tokens[1].token_type, TokenType::ColonEq);
4235 assert_eq!(tokens[2].token_type, TokenType::Number);
4236
4237 let tokens = tokenizer.tokenize("a:b").unwrap();
4239 assert!(tokens.iter().any(|t| t.token_type == TokenType::Colon));
4240 assert!(!tokens.iter().any(|t| t.token_type == TokenType::ColonEq));
4241
4242 let tokens = tokenizer.tokenize("a::INT").unwrap();
4244 assert!(tokens.iter().any(|t| t.token_type == TokenType::DColon));
4245 }
4246
4247 #[test]
4248 fn test_colon_eq_parsing() {
4249 use crate::generator::Generator;
4250 use crate::parser::Parser;
4251
4252 let ast = Parser::parse_sql("SELECT @var1 := 1, @var2")
4254 .expect("Failed to parse MySQL @var := expr");
4255 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4256 assert_eq!(output, "SELECT @var1 := 1, @var2");
4257
4258 let ast = Parser::parse_sql("SELECT @var1, @var2 := @var1")
4260 .expect("Failed to parse MySQL @var2 := @var1");
4261 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4262 assert_eq!(output, "SELECT @var1, @var2 := @var1");
4263
4264 let ast = Parser::parse_sql("SELECT @var1 := COUNT(*) FROM t1")
4266 .expect("Failed to parse MySQL @var := COUNT(*)");
4267 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4268 assert_eq!(output, "SELECT @var1 := COUNT(*) FROM t1");
4269
4270 let ast = Parser::parse_sql("SET @var1 := 1").expect("Failed to parse SET @var1 := 1");
4272 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4273 assert_eq!(output, "SET @var1 = 1");
4274
4275 let ast =
4277 Parser::parse_sql("UNION_VALUE(k1 := 1)").expect("Failed to parse named arg with :=");
4278 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4279 assert_eq!(output, "UNION_VALUE(k1 := 1)");
4280
4281 let ast = Parser::parse_sql("SELECT UNNEST(col, recursive := TRUE) FROM t")
4283 .expect("Failed to parse UNNEST with :=");
4284 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4285 assert_eq!(output, "SELECT UNNEST(col, recursive := TRUE) FROM t");
4286
4287 let ast =
4289 Parser::parse_sql("SELECT foo: 1").expect("Failed to parse DuckDB prefix alias foo: 1");
4290 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4291 assert_eq!(output, "SELECT 1 AS foo");
4292
4293 let ast = Parser::parse_sql("SELECT foo: 1, bar: 2, baz: 3")
4295 .expect("Failed to parse DuckDB multiple prefix aliases");
4296 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4297 assert_eq!(output, "SELECT 1 AS foo, 2 AS bar, 3 AS baz");
4298 }
4299
4300 #[test]
4301 fn test_colon_eq_dialect_roundtrip() {
4302 use crate::dialects::{Dialect, DialectType};
4303
4304 fn check(dialect: DialectType, sql: &str, expected: Option<&str>) {
4305 let d = Dialect::get(dialect);
4306 let ast = d
4307 .parse(sql)
4308 .unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
4309 assert!(!ast.is_empty(), "Empty AST for: {}", sql);
4310 let transformed = d
4311 .transform(ast[0].clone())
4312 .unwrap_or_else(|e| panic!("Transform error for '{}': {}", sql, e));
4313 let output = d
4314 .generate(&transformed)
4315 .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e));
4316 let expected = expected.unwrap_or(sql);
4317 assert_eq!(output, expected, "Roundtrip failed for: {}", sql);
4318 }
4319
4320 check(DialectType::MySQL, "SELECT @var1 := 1, @var2", None);
4322 check(DialectType::MySQL, "SELECT @var1, @var2 := @var1", None);
4323 check(DialectType::MySQL, "SELECT @var1 := COUNT(*) FROM t1", None);
4324 check(DialectType::MySQL, "SET @var1 := 1", Some("SET @var1 = 1"));
4325
4326 check(
4328 DialectType::DuckDB,
4329 "SELECT UNNEST(col, recursive := TRUE) FROM t",
4330 None,
4331 );
4332 check(DialectType::DuckDB, "UNION_VALUE(k1 := 1)", None);
4333
4334 {
4337 let d = Dialect::get(DialectType::DuckDB);
4338 let ast = d
4339 .parse("STRUCT_PACK(a := 'b')::json")
4340 .expect("Failed to parse STRUCT_PACK(a := 'b')::json");
4341 assert!(!ast.is_empty(), "Empty AST for STRUCT_PACK(a := 'b')::json");
4342 }
4343
4344 check(
4346 DialectType::DuckDB,
4347 "SELECT foo: 1",
4348 Some("SELECT 1 AS foo"),
4349 );
4350 check(
4351 DialectType::DuckDB,
4352 "SELECT foo: 1, bar: 2, baz: 3",
4353 Some("SELECT 1 AS foo, 2 AS bar, 3 AS baz"),
4354 );
4355 }
4356
4357 #[test]
4358 fn test_comment_roundtrip() {
4359 use crate::generator::Generator;
4360 use crate::parser::Parser;
4361
4362 fn check_roundtrip(sql: &str) -> Option<String> {
4363 let ast = match Parser::parse_sql(sql) {
4364 Ok(a) => a,
4365 Err(e) => return Some(format!("Parse error: {:?}", e)),
4366 };
4367 if ast.is_empty() {
4368 return Some("Empty AST".to_string());
4369 }
4370 let mut generator = Generator::default();
4371 let output = match generator.generate(&ast[0]) {
4372 Ok(o) => o,
4373 Err(e) => return Some(format!("Gen error: {:?}", e)),
4374 };
4375 if output == sql {
4376 None
4377 } else {
4378 Some(format!(
4379 "Mismatch:\n input: {}\n output: {}",
4380 sql, output
4381 ))
4382 }
4383 }
4384
4385 let tests = vec![
4386 "SELECT c /* c1 */ AS alias /* c2 */",
4392 "SELECT a /* x */, b /* x */",
4394 "SELECT a /* x */ /* y */ /* z */, b /* k */ /* m */",
4396 "SELECT * FROM foo /* x */, bla /* x */",
4398 "SELECT 1 /* comment */ + 1",
4400 "SELECT 1 /* c1 */ + 2 /* c2 */",
4401 "SELECT 1 /* c1 */ + /* c2 */ 2 /* c3 */",
4402 "SELECT CAST(x AS INT) /* comment */ FROM foo",
4404 "SELECT FOO(x /* c */) /* FOO */, b /* b */",
4406 "SELECT x FROM a.b.c /* x */, e.f.g /* x */",
4408 "INSERT INTO t1 (tc1 /* tc1 */, tc2 /* tc2 */) SELECT c1 /* sc1 */, c2 /* sc2 */ FROM t",
4410 "/* c */ WITH x AS (SELECT 1) SELECT * FROM x",
4412 "/* comment1 */ INSERT INTO x /* comment2 */ VALUES (1, 2, 3)",
4413 "/* comment1 */ UPDATE tbl /* comment2 */ SET x = 2 WHERE x < 2",
4414 "/* comment1 */ DELETE FROM x /* comment2 */ WHERE y > 1",
4415 "/* comment */ CREATE TABLE foo AS SELECT 1",
4416 "INSERT INTO foo SELECT * FROM bar /* comment */",
4418 "SELECT FOO(x /* c1 */ + y /* c2 */ + BLA(5 /* c3 */)) FROM (VALUES (1 /* c4 */, \"test\" /* c5 */)) /* c6 */",
4420 ];
4421
4422 let mut failures = Vec::new();
4423 for sql in tests {
4424 if let Some(e) = check_roundtrip(sql) {
4425 failures.push(e);
4426 }
4427 }
4428
4429 if !failures.is_empty() {
4430 panic!("Comment roundtrip failures:\n{}", failures.join("\n\n"));
4431 }
4432 }
4433
4434 #[test]
4435 fn test_dollar_quoted_string_parsing() {
4436 use crate::dialects::{Dialect, DialectType};
4437
4438 let (tag, content) = super::parse_dollar_string_token("FOO\x00content here");
4440 assert_eq!(tag, Some("FOO".to_string()));
4441 assert_eq!(content, "content here");
4442
4443 let (tag, content) = super::parse_dollar_string_token("just content");
4444 assert_eq!(tag, None);
4445 assert_eq!(content, "just content");
4446
4447 fn check_databricks(sql: &str, expected: Option<&str>) {
4449 let d = Dialect::get(DialectType::Databricks);
4450 let ast = d
4451 .parse(sql)
4452 .unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
4453 assert!(!ast.is_empty(), "Empty AST for: {}", sql);
4454 let transformed = d
4455 .transform(ast[0].clone())
4456 .unwrap_or_else(|e| panic!("Transform error for '{}': {}", sql, e));
4457 let output = d
4458 .generate(&transformed)
4459 .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e));
4460 let expected = expected.unwrap_or(sql);
4461 assert_eq!(output, expected, "Roundtrip failed for: {}", sql);
4462 }
4463
4464 check_databricks(
4466 "CREATE FUNCTION add_one(x INT) RETURNS INT LANGUAGE PYTHON AS $$def add_one(x):\n return x+1$$",
4467 None
4468 );
4469
4470 check_databricks(
4472 "CREATE FUNCTION add_one(x INT) RETURNS INT LANGUAGE PYTHON AS $FOO$def add_one(x):\n return x+1$FOO$",
4473 None
4474 );
4475 }
4476
4477 #[test]
4478 fn test_numeric_underscore_stripping() {
4479 let mut config = TokenizerConfig::default();
4481 config.numbers_can_be_underscore_separated = true;
4482 let tokenizer = Tokenizer::new(config);
4483
4484 let tokens = tokenizer.tokenize("SELECT 1_2_3_4_5").unwrap();
4486 assert_eq!(tokens[1].token_type, TokenType::Number);
4487 assert_eq!(tokens[1].text, "12345");
4488
4489 let tokens = tokenizer.tokenize("SELECT 20_000").unwrap();
4491 assert_eq!(tokens[1].token_type, TokenType::Number);
4492 assert_eq!(tokens[1].text, "20000");
4493
4494 let tokens = tokenizer.tokenize("SELECT 1_2E+1_0").unwrap();
4496 assert_eq!(tokens[1].token_type, TokenType::Number);
4497 assert_eq!(tokens[1].text, "12E+10");
4498
4499 let default_tokenizer = Tokenizer::default();
4501 let tokens = default_tokenizer.tokenize("SELECT 1_2_3_4_5").unwrap();
4502 assert_eq!(tokens[1].token_type, TokenType::Number);
4503 assert_eq!(tokens[1].text, "1_2_3_4_5");
4504 }
4505}