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