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,
853}
854
855impl TokenType {
856 pub fn is_keyword(&self) -> bool {
858 matches!(
859 self,
860 TokenType::Select
861 | TokenType::From
862 | TokenType::Where
863 | TokenType::And
864 | TokenType::Or
865 | TokenType::Not
866 | TokenType::In
867 | TokenType::Is
868 | TokenType::Null
869 | TokenType::True
870 | TokenType::False
871 | TokenType::As
872 | TokenType::On
873 | TokenType::Join
874 | TokenType::Left
875 | TokenType::Right
876 | TokenType::Inner
877 | TokenType::Outer
878 | TokenType::Full
879 | TokenType::Cross
880 | TokenType::Semi
881 | TokenType::Anti
882 | TokenType::Union
883 | TokenType::Except
884 | TokenType::Intersect
885 | TokenType::GroupBy
886 | TokenType::OrderBy
887 | TokenType::Having
888 | TokenType::Limit
889 | TokenType::Offset
890 | TokenType::Case
891 | TokenType::When
892 | TokenType::Then
893 | TokenType::Else
894 | TokenType::End
895 | TokenType::Create
896 | TokenType::Drop
897 | TokenType::Alter
898 | TokenType::Insert
899 | TokenType::Update
900 | TokenType::Delete
901 | TokenType::Into
902 | TokenType::Values
903 | TokenType::Set
904 | TokenType::With
905 | TokenType::Distinct
906 | TokenType::All
907 | TokenType::Exists
908 | TokenType::Between
909 | TokenType::Like
910 | TokenType::ILike
911 | TokenType::Filter
913 | TokenType::Date
914 | TokenType::Timestamp
915 | TokenType::TimestampTz
916 | TokenType::Interval
917 | TokenType::Time
918 | TokenType::Table
919 | TokenType::Index
920 | TokenType::Column
921 | TokenType::Database
922 | TokenType::Schema
923 | TokenType::View
924 | TokenType::Function
925 | TokenType::Procedure
926 | TokenType::Trigger
927 | TokenType::Sequence
928 | TokenType::Over
929 | TokenType::Partition
930 | TokenType::Window
931 | TokenType::Rows
932 | TokenType::Range
933 | TokenType::First
934 | TokenType::Last
935 | TokenType::Preceding
936 | TokenType::Following
937 | TokenType::Current
938 | TokenType::Row
939 | TokenType::Unbounded
940 | TokenType::Array
941 | TokenType::Struct
942 | TokenType::Map
943 | TokenType::PrimaryKey
944 | TokenType::Key
945 | TokenType::ForeignKey
946 | TokenType::References
947 | TokenType::Unique
948 | TokenType::Check
949 | TokenType::Default
950 | TokenType::Constraint
951 | TokenType::Comment
952 | TokenType::Rollup
953 | TokenType::Cube
954 | TokenType::Grant
955 | TokenType::Revoke
956 | TokenType::Type
957 | TokenType::Use
958 | TokenType::Cache
959 | TokenType::Uncache
960 | TokenType::Load
961 | TokenType::Any
962 | TokenType::Some
963 | TokenType::Asc
964 | TokenType::Desc
965 | TokenType::Nulls
966 | TokenType::Lateral
967 | TokenType::Natural
968 | TokenType::Escape
969 | TokenType::Glob
970 | TokenType::Match
971 | TokenType::Recursive
972 | TokenType::Replace
973 | TokenType::Returns
974 | TokenType::If
975 | TokenType::Pivot
976 | TokenType::Unpivot
977 | TokenType::Json
978 | TokenType::Blob
979 | TokenType::Text
980 | TokenType::Int
981 | TokenType::BigInt
982 | TokenType::SmallInt
983 | TokenType::TinyInt
984 | TokenType::Int128
985 | TokenType::UInt128
986 | TokenType::Int256
987 | TokenType::UInt256
988 | TokenType::UInt
989 | TokenType::UBigInt
990 | TokenType::Float
991 | TokenType::Double
992 | TokenType::Decimal
993 | TokenType::Boolean
994 | TokenType::VarChar
995 | TokenType::Char
996 | TokenType::Binary
997 | TokenType::VarBinary
998 | TokenType::No
999 | TokenType::DateTime
1000 | TokenType::Truncate
1001 | TokenType::Execute
1002 | TokenType::Merge
1003 | TokenType::Top
1004 | TokenType::Begin
1005 | TokenType::Generated
1006 | TokenType::Identity
1007 | TokenType::Always
1008 | TokenType::Extract
1009 | TokenType::AsOf
1011 | TokenType::Prior
1012 | TokenType::After
1013 | TokenType::Restrict
1014 | TokenType::Cascade
1015 | TokenType::Local
1016 | TokenType::Rename
1017 | TokenType::Enum
1018 | TokenType::Within
1019 | TokenType::Format
1020 | TokenType::Final
1021 | TokenType::FileFormat
1022 | TokenType::Input
1023 | TokenType::InputFormat
1024 | TokenType::Copy
1025 | TokenType::Put
1026 | TokenType::Get
1027 | TokenType::Show
1028 | TokenType::Serde
1029 | TokenType::Sample
1030 | TokenType::Sort
1031 | TokenType::Collate
1032 | TokenType::Ties
1033 | TokenType::IsNull
1034 | TokenType::NotNull
1035 | TokenType::Exclude
1036 | TokenType::Temporary
1037 | TokenType::Add
1038 | TokenType::Ordinality
1039 | TokenType::Overlaps
1040 | TokenType::Block
1041 | TokenType::Pattern
1042 | TokenType::Group
1043 | TokenType::Cluster
1044 | TokenType::Repeatable
1045 | TokenType::Groups
1046 | TokenType::Commit
1047 | TokenType::Warehouse
1048 | TokenType::System
1049 | TokenType::By
1050 | TokenType::To
1051 | TokenType::Fetch
1052 | TokenType::For
1053 | TokenType::Only
1054 | TokenType::Next
1055 | TokenType::Lock
1056 | TokenType::Refresh
1057 | TokenType::Settings
1058 | TokenType::Operator
1059 | TokenType::Overwrite
1060 | TokenType::StraightJoin
1061 | TokenType::Start
1062 | TokenType::Ignore
1064 | TokenType::Domain
1065 | TokenType::Apply
1066 | TokenType::Respect
1067 | TokenType::Materialized
1068 | TokenType::Prewhere
1069 | TokenType::Old
1070 | TokenType::New
1071 | TokenType::Cast
1072 | TokenType::TryCast
1073 | TokenType::SafeCast
1074 | TokenType::Transaction
1075 | TokenType::Describe
1076 | TokenType::Kill
1077 | TokenType::Lambda
1078 | TokenType::Declare
1079 | TokenType::Keep
1080 | TokenType::Output
1081 | TokenType::Percent
1082 | TokenType::Qualify
1083 | TokenType::Returning
1084 | TokenType::Language
1085 | TokenType::Prepare
1086 | TokenType::Preserve
1087 | TokenType::Savepoint
1088 | TokenType::Rollback
1089 | TokenType::Body
1090 | TokenType::Increment
1091 | TokenType::Minvalue
1092 | TokenType::Maxvalue
1093 | TokenType::Cycle
1094 | TokenType::NoCycle
1095 | TokenType::Seed
1096 | TokenType::Namespace
1097 | TokenType::Authorization
1098 | TokenType::Order
1099 | TokenType::Restart
1100 | TokenType::Before
1101 | TokenType::Instead
1102 | TokenType::Each
1103 | TokenType::Statement
1104 | TokenType::Referencing
1105 | TokenType::Of
1106 | TokenType::Separator
1107 | TokenType::Others
1108 | TokenType::Placing
1109 | TokenType::Owned
1110 | TokenType::Running
1111 | TokenType::Define
1112 | TokenType::Measures
1113 | TokenType::MatchRecognize
1114 | TokenType::AutoIncrement
1115 | TokenType::Connect
1116 | TokenType::Distribute
1117 | TokenType::Bernoulli
1118 | TokenType::TableSample
1119 | TokenType::Inpath
1120 | TokenType::Pragma
1121 | TokenType::Siblings
1122 | TokenType::SerdeProperties
1123 | TokenType::RLike
1124 )
1125 }
1126
1127 pub fn is_comparison(&self) -> bool {
1129 matches!(
1130 self,
1131 TokenType::Eq
1132 | TokenType::Neq
1133 | TokenType::Lt
1134 | TokenType::Lte
1135 | TokenType::Gt
1136 | TokenType::Gte
1137 | TokenType::NullsafeEq
1138 )
1139 }
1140
1141 pub fn is_arithmetic(&self) -> bool {
1143 matches!(
1144 self,
1145 TokenType::Plus
1146 | TokenType::Dash
1147 | TokenType::Star
1148 | TokenType::Slash
1149 | TokenType::Percent
1150 | TokenType::Mod
1151 | TokenType::Div
1152 )
1153 }
1154}
1155
1156impl fmt::Display for TokenType {
1157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1158 write!(f, "{:?}", self)
1159 }
1160}
1161
1162static DEFAULT_KEYWORDS: LazyLock<HashMap<String, TokenType>> = LazyLock::new(|| {
1165 let mut keywords = HashMap::with_capacity(300);
1166 keywords.insert("SELECT".to_string(), TokenType::Select);
1168 keywords.insert("FROM".to_string(), TokenType::From);
1169 keywords.insert("WHERE".to_string(), TokenType::Where);
1170 keywords.insert("AND".to_string(), TokenType::And);
1171 keywords.insert("OR".to_string(), TokenType::Or);
1172 keywords.insert("NOT".to_string(), TokenType::Not);
1173 keywords.insert("AS".to_string(), TokenType::As);
1174 keywords.insert("ON".to_string(), TokenType::On);
1175 keywords.insert("JOIN".to_string(), TokenType::Join);
1176 keywords.insert("LEFT".to_string(), TokenType::Left);
1177 keywords.insert("RIGHT".to_string(), TokenType::Right);
1178 keywords.insert("INNER".to_string(), TokenType::Inner);
1179 keywords.insert("OUTER".to_string(), TokenType::Outer);
1180 keywords.insert("OUTPUT".to_string(), TokenType::Output);
1181 keywords.insert("FULL".to_string(), TokenType::Full);
1182 keywords.insert("CROSS".to_string(), TokenType::Cross);
1183 keywords.insert("SEMI".to_string(), TokenType::Semi);
1184 keywords.insert("ANTI".to_string(), TokenType::Anti);
1185 keywords.insert("STRAIGHT_JOIN".to_string(), TokenType::StraightJoin);
1186 keywords.insert("UNION".to_string(), TokenType::Union);
1187 keywords.insert("EXCEPT".to_string(), TokenType::Except);
1188 keywords.insert("MINUS".to_string(), TokenType::Except); keywords.insert("INTERSECT".to_string(), TokenType::Intersect);
1190 keywords.insert("GROUP".to_string(), TokenType::Group);
1191 keywords.insert("CUBE".to_string(), TokenType::Cube);
1192 keywords.insert("ROLLUP".to_string(), TokenType::Rollup);
1193 keywords.insert("WITHIN".to_string(), TokenType::Within);
1194 keywords.insert("ORDER".to_string(), TokenType::Order);
1195 keywords.insert("BY".to_string(), TokenType::By);
1196 keywords.insert("HAVING".to_string(), TokenType::Having);
1197 keywords.insert("LIMIT".to_string(), TokenType::Limit);
1198 keywords.insert("OFFSET".to_string(), TokenType::Offset);
1199 keywords.insert("ORDINALITY".to_string(), TokenType::Ordinality);
1200 keywords.insert("FETCH".to_string(), TokenType::Fetch);
1201 keywords.insert("FIRST".to_string(), TokenType::First);
1202 keywords.insert("NEXT".to_string(), TokenType::Next);
1203 keywords.insert("ONLY".to_string(), TokenType::Only);
1204 keywords.insert("KEEP".to_string(), TokenType::Keep);
1205 keywords.insert("IGNORE".to_string(), TokenType::Ignore);
1206 keywords.insert("INPUT".to_string(), TokenType::Input);
1207 keywords.insert("CASE".to_string(), TokenType::Case);
1208 keywords.insert("WHEN".to_string(), TokenType::When);
1209 keywords.insert("THEN".to_string(), TokenType::Then);
1210 keywords.insert("ELSE".to_string(), TokenType::Else);
1211 keywords.insert("END".to_string(), TokenType::End);
1212 keywords.insert("ENDIF".to_string(), TokenType::End); keywords.insert("NULL".to_string(), TokenType::Null);
1214 keywords.insert("TRUE".to_string(), TokenType::True);
1215 keywords.insert("FALSE".to_string(), TokenType::False);
1216 keywords.insert("IS".to_string(), TokenType::Is);
1217 keywords.insert("IN".to_string(), TokenType::In);
1218 keywords.insert("BETWEEN".to_string(), TokenType::Between);
1219 keywords.insert("OVERLAPS".to_string(), TokenType::Overlaps);
1220 keywords.insert("LIKE".to_string(), TokenType::Like);
1221 keywords.insert("ILIKE".to_string(), TokenType::ILike);
1222 keywords.insert("RLIKE".to_string(), TokenType::RLike);
1223 keywords.insert("REGEXP".to_string(), TokenType::RLike);
1224 keywords.insert("ESCAPE".to_string(), TokenType::Escape);
1225 keywords.insert("EXISTS".to_string(), TokenType::Exists);
1226 keywords.insert("DISTINCT".to_string(), TokenType::Distinct);
1227 keywords.insert("ALL".to_string(), TokenType::All);
1228 keywords.insert("WITH".to_string(), TokenType::With);
1229 keywords.insert("CREATE".to_string(), TokenType::Create);
1230 keywords.insert("DROP".to_string(), TokenType::Drop);
1231 keywords.insert("ALTER".to_string(), TokenType::Alter);
1232 keywords.insert("TRUNCATE".to_string(), TokenType::Truncate);
1233 keywords.insert("TABLE".to_string(), TokenType::Table);
1234 keywords.insert("VIEW".to_string(), TokenType::View);
1235 keywords.insert("INDEX".to_string(), TokenType::Index);
1236 keywords.insert("COLUMN".to_string(), TokenType::Column);
1237 keywords.insert("CONSTRAINT".to_string(), TokenType::Constraint);
1238 keywords.insert("ADD".to_string(), TokenType::Add);
1239 keywords.insert("CASCADE".to_string(), TokenType::Cascade);
1240 keywords.insert("RESTRICT".to_string(), TokenType::Restrict);
1241 keywords.insert("RENAME".to_string(), TokenType::Rename);
1242 keywords.insert("TEMPORARY".to_string(), TokenType::Temporary);
1243 keywords.insert("TEMP".to_string(), TokenType::Temporary);
1244 keywords.insert("UNIQUE".to_string(), TokenType::Unique);
1245 keywords.insert("PRIMARY".to_string(), TokenType::PrimaryKey);
1246 keywords.insert("FOREIGN".to_string(), TokenType::ForeignKey);
1247 keywords.insert("KEY".to_string(), TokenType::Key);
1248 keywords.insert("KILL".to_string(), TokenType::Kill);
1249 keywords.insert("REFERENCES".to_string(), TokenType::References);
1250 keywords.insert("DEFAULT".to_string(), TokenType::Default);
1251 keywords.insert("DECLARE".to_string(), TokenType::Declare);
1252 keywords.insert("AUTO_INCREMENT".to_string(), TokenType::AutoIncrement);
1253 keywords.insert("AUTOINCREMENT".to_string(), TokenType::AutoIncrement); keywords.insert("MATERIALIZED".to_string(), TokenType::Materialized);
1255 keywords.insert("REPLACE".to_string(), TokenType::Replace);
1256 keywords.insert("TO".to_string(), TokenType::To);
1257 keywords.insert("INSERT".to_string(), TokenType::Insert);
1258 keywords.insert("OVERWRITE".to_string(), TokenType::Overwrite);
1259 keywords.insert("UPDATE".to_string(), TokenType::Update);
1260 keywords.insert("USE".to_string(), TokenType::Use);
1261 keywords.insert("WAREHOUSE".to_string(), TokenType::Warehouse);
1262 keywords.insert("GLOB".to_string(), TokenType::Glob);
1263 keywords.insert("DELETE".to_string(), TokenType::Delete);
1264 keywords.insert("MERGE".to_string(), TokenType::Merge);
1265 keywords.insert("CACHE".to_string(), TokenType::Cache);
1266 keywords.insert("UNCACHE".to_string(), TokenType::Uncache);
1267 keywords.insert("REFRESH".to_string(), TokenType::Refresh);
1268 keywords.insert("GRANT".to_string(), TokenType::Grant);
1269 keywords.insert("REVOKE".to_string(), TokenType::Revoke);
1270 keywords.insert("COMMENT".to_string(), TokenType::Comment);
1271 keywords.insert("COLLATE".to_string(), TokenType::Collate);
1272 keywords.insert("INTO".to_string(), TokenType::Into);
1273 keywords.insert("VALUES".to_string(), TokenType::Values);
1274 keywords.insert("SET".to_string(), TokenType::Set);
1275 keywords.insert("SETTINGS".to_string(), TokenType::Settings);
1276 keywords.insert("SEPARATOR".to_string(), TokenType::Separator);
1277 keywords.insert("ASC".to_string(), TokenType::Asc);
1278 keywords.insert("DESC".to_string(), TokenType::Desc);
1279 keywords.insert("NULLS".to_string(), TokenType::Nulls);
1280 keywords.insert("RESPECT".to_string(), TokenType::Respect);
1281 keywords.insert("FIRST".to_string(), TokenType::First);
1282 keywords.insert("LAST".to_string(), TokenType::Last);
1283 keywords.insert("IF".to_string(), TokenType::If);
1284 keywords.insert("CAST".to_string(), TokenType::Cast);
1285 keywords.insert("TRY_CAST".to_string(), TokenType::TryCast);
1286 keywords.insert("SAFE_CAST".to_string(), TokenType::SafeCast);
1287 keywords.insert("OVER".to_string(), TokenType::Over);
1288 keywords.insert("PARTITION".to_string(), TokenType::Partition);
1289 keywords.insert("PLACING".to_string(), TokenType::Placing);
1290 keywords.insert("WINDOW".to_string(), TokenType::Window);
1291 keywords.insert("ROWS".to_string(), TokenType::Rows);
1292 keywords.insert("RANGE".to_string(), TokenType::Range);
1293 keywords.insert("FILTER".to_string(), TokenType::Filter);
1294 keywords.insert("NATURAL".to_string(), TokenType::Natural);
1295 keywords.insert("USING".to_string(), TokenType::Using);
1296 keywords.insert("UNBOUNDED".to_string(), TokenType::Unbounded);
1297 keywords.insert("PRECEDING".to_string(), TokenType::Preceding);
1298 keywords.insert("FOLLOWING".to_string(), TokenType::Following);
1299 keywords.insert("CURRENT".to_string(), TokenType::Current);
1300 keywords.insert("ROW".to_string(), TokenType::Row);
1301 keywords.insert("GROUPS".to_string(), TokenType::Groups);
1302 keywords.insert("RECURSIVE".to_string(), TokenType::Recursive);
1303 keywords.insert("BOTH".to_string(), TokenType::Both);
1305 keywords.insert("LEADING".to_string(), TokenType::Leading);
1306 keywords.insert("TRAILING".to_string(), TokenType::Trailing);
1307 keywords.insert("INTERVAL".to_string(), TokenType::Interval);
1308 keywords.insert("TOP".to_string(), TokenType::Top);
1310 keywords.insert("QUALIFY".to_string(), TokenType::Qualify);
1311 keywords.insert("SAMPLE".to_string(), TokenType::Sample);
1312 keywords.insert("TABLESAMPLE".to_string(), TokenType::TableSample);
1313 keywords.insert("BERNOULLI".to_string(), TokenType::Bernoulli);
1314 keywords.insert("SYSTEM".to_string(), TokenType::System);
1315 keywords.insert("BLOCK".to_string(), TokenType::Block);
1316 keywords.insert("TIES".to_string(), TokenType::Ties);
1317 keywords.insert("LATERAL".to_string(), TokenType::Lateral);
1318 keywords.insert("LAMBDA".to_string(), TokenType::Lambda);
1319 keywords.insert("APPLY".to_string(), TokenType::Apply);
1320 keywords.insert("CONNECT".to_string(), TokenType::Connect);
1322 keywords.insert("CLUSTER".to_string(), TokenType::Cluster);
1324 keywords.insert("DISTRIBUTE".to_string(), TokenType::Distribute);
1325 keywords.insert("SORT".to_string(), TokenType::Sort);
1326 keywords.insert("PIVOT".to_string(), TokenType::Pivot);
1327 keywords.insert("PREWHERE".to_string(), TokenType::Prewhere);
1328 keywords.insert("UNPIVOT".to_string(), TokenType::Unpivot);
1329 keywords.insert("FOR".to_string(), TokenType::For);
1330 keywords.insert("ANY".to_string(), TokenType::Any);
1331 keywords.insert("SOME".to_string(), TokenType::Some);
1332 keywords.insert("ASOF".to_string(), TokenType::AsOf);
1333 keywords.insert("PERCENT".to_string(), TokenType::Percent);
1334 keywords.insert("EXCLUDE".to_string(), TokenType::Exclude);
1335 keywords.insert("NO".to_string(), TokenType::No);
1336 keywords.insert("OTHERS".to_string(), TokenType::Others);
1337 keywords.insert("OPERATOR".to_string(), TokenType::Operator);
1339 keywords.insert("SCHEMA".to_string(), TokenType::Schema);
1341 keywords.insert("NAMESPACE".to_string(), TokenType::Namespace);
1342 keywords.insert("DATABASE".to_string(), TokenType::Database);
1343 keywords.insert("FUNCTION".to_string(), TokenType::Function);
1344 keywords.insert("PROCEDURE".to_string(), TokenType::Procedure);
1345 keywords.insert("PROC".to_string(), TokenType::Procedure);
1346 keywords.insert("SEQUENCE".to_string(), TokenType::Sequence);
1347 keywords.insert("TRIGGER".to_string(), TokenType::Trigger);
1348 keywords.insert("TYPE".to_string(), TokenType::Type);
1349 keywords.insert("DOMAIN".to_string(), TokenType::Domain);
1350 keywords.insert("RETURNS".to_string(), TokenType::Returns);
1351 keywords.insert("RETURNING".to_string(), TokenType::Returning);
1352 keywords.insert("LANGUAGE".to_string(), TokenType::Language);
1353 keywords.insert("ROLLBACK".to_string(), TokenType::Rollback);
1354 keywords.insert("COMMIT".to_string(), TokenType::Commit);
1355 keywords.insert("BEGIN".to_string(), TokenType::Begin);
1356 keywords.insert("DESCRIBE".to_string(), TokenType::Describe);
1357 keywords.insert("PREPARE".to_string(), TokenType::Prepare);
1358 keywords.insert("PRESERVE".to_string(), TokenType::Preserve);
1359 keywords.insert("TRANSACTION".to_string(), TokenType::Transaction);
1360 keywords.insert("SAVEPOINT".to_string(), TokenType::Savepoint);
1361 keywords.insert("BODY".to_string(), TokenType::Body);
1362 keywords.insert("INCREMENT".to_string(), TokenType::Increment);
1363 keywords.insert("MINVALUE".to_string(), TokenType::Minvalue);
1364 keywords.insert("MAXVALUE".to_string(), TokenType::Maxvalue);
1365 keywords.insert("CYCLE".to_string(), TokenType::Cycle);
1366 keywords.insert("NOCYCLE".to_string(), TokenType::NoCycle);
1367 keywords.insert("PRIOR".to_string(), TokenType::Prior);
1368 keywords.insert("MATCH".to_string(), TokenType::Match);
1370 keywords.insert("MATCH_RECOGNIZE".to_string(), TokenType::MatchRecognize);
1371 keywords.insert("MEASURES".to_string(), TokenType::Measures);
1372 keywords.insert("PATTERN".to_string(), TokenType::Pattern);
1373 keywords.insert("DEFINE".to_string(), TokenType::Define);
1374 keywords.insert("RUNNING".to_string(), TokenType::Running);
1375 keywords.insert("FINAL".to_string(), TokenType::Final);
1376 keywords.insert("OWNED".to_string(), TokenType::Owned);
1377 keywords.insert("AFTER".to_string(), TokenType::After);
1378 keywords.insert("BEFORE".to_string(), TokenType::Before);
1379 keywords.insert("INSTEAD".to_string(), TokenType::Instead);
1380 keywords.insert("EACH".to_string(), TokenType::Each);
1381 keywords.insert("STATEMENT".to_string(), TokenType::Statement);
1382 keywords.insert("REFERENCING".to_string(), TokenType::Referencing);
1383 keywords.insert("OLD".to_string(), TokenType::Old);
1384 keywords.insert("NEW".to_string(), TokenType::New);
1385 keywords.insert("OF".to_string(), TokenType::Of);
1386 keywords.insert("CHECK".to_string(), TokenType::Check);
1387 keywords.insert("START".to_string(), TokenType::Start);
1388 keywords.insert("ENUM".to_string(), TokenType::Enum);
1389 keywords.insert("AUTHORIZATION".to_string(), TokenType::Authorization);
1390 keywords.insert("RESTART".to_string(), TokenType::Restart);
1391 keywords.insert("DATE".to_string(), TokenType::Date);
1393 keywords.insert("TIME".to_string(), TokenType::Time);
1394 keywords.insert("TIMESTAMP".to_string(), TokenType::Timestamp);
1395 keywords.insert("DATETIME".to_string(), TokenType::DateTime);
1396 keywords.insert("GENERATED".to_string(), TokenType::Generated);
1397 keywords.insert("IDENTITY".to_string(), TokenType::Identity);
1398 keywords.insert("ALWAYS".to_string(), TokenType::Always);
1399 keywords.insert("LOAD".to_string(), TokenType::Load);
1401 keywords.insert("LOCAL".to_string(), TokenType::Local);
1402 keywords.insert("INPATH".to_string(), TokenType::Inpath);
1403 keywords.insert("INPUTFORMAT".to_string(), TokenType::InputFormat);
1404 keywords.insert("SERDE".to_string(), TokenType::Serde);
1405 keywords.insert("SERDEPROPERTIES".to_string(), TokenType::SerdeProperties);
1406 keywords.insert("FORMAT".to_string(), TokenType::Format);
1407 keywords.insert("PRAGMA".to_string(), TokenType::Pragma);
1409 keywords.insert("SHOW".to_string(), TokenType::Show);
1411 keywords.insert("SIBLINGS".to_string(), TokenType::Siblings);
1413 keywords.insert("COPY".to_string(), TokenType::Copy);
1415 keywords.insert("PUT".to_string(), TokenType::Put);
1416 keywords.insert("GET".to_string(), TokenType::Get);
1417 keywords.insert("EXEC".to_string(), TokenType::Execute);
1419 keywords.insert("EXECUTE".to_string(), TokenType::Execute);
1420 keywords.insert("ISNULL".to_string(), TokenType::IsNull);
1422 keywords.insert("NOTNULL".to_string(), TokenType::NotNull);
1423 keywords
1424});
1425
1426static DEFAULT_SINGLE_TOKENS: LazyLock<HashMap<char, TokenType>> = LazyLock::new(|| {
1427 let mut single_tokens = HashMap::with_capacity(30);
1428 single_tokens.insert('(', TokenType::LParen);
1429 single_tokens.insert(')', TokenType::RParen);
1430 single_tokens.insert('[', TokenType::LBracket);
1431 single_tokens.insert(']', TokenType::RBracket);
1432 single_tokens.insert('{', TokenType::LBrace);
1433 single_tokens.insert('}', TokenType::RBrace);
1434 single_tokens.insert(',', TokenType::Comma);
1435 single_tokens.insert('.', TokenType::Dot);
1436 single_tokens.insert(';', TokenType::Semicolon);
1437 single_tokens.insert('+', TokenType::Plus);
1438 single_tokens.insert('-', TokenType::Dash);
1439 single_tokens.insert('*', TokenType::Star);
1440 single_tokens.insert('/', TokenType::Slash);
1441 single_tokens.insert('%', TokenType::Percent);
1442 single_tokens.insert('&', TokenType::Amp);
1443 single_tokens.insert('|', TokenType::Pipe);
1444 single_tokens.insert('^', TokenType::Caret);
1445 single_tokens.insert('~', TokenType::Tilde);
1446 single_tokens.insert('<', TokenType::Lt);
1447 single_tokens.insert('>', TokenType::Gt);
1448 single_tokens.insert('=', TokenType::Eq);
1449 single_tokens.insert('!', TokenType::Exclamation);
1450 single_tokens.insert(':', TokenType::Colon);
1451 single_tokens.insert('@', TokenType::DAt);
1452 single_tokens.insert('#', TokenType::Hash);
1453 single_tokens.insert('$', TokenType::Dollar);
1454 single_tokens.insert('?', TokenType::Parameter);
1455 single_tokens
1456});
1457
1458static DEFAULT_QUOTES: LazyLock<HashMap<String, String>> = LazyLock::new(|| {
1459 let mut quotes = HashMap::with_capacity(4);
1460 quotes.insert("'".to_string(), "'".to_string());
1461 quotes.insert("\"\"\"".to_string(), "\"\"\"".to_string());
1463 quotes
1464});
1465
1466static DEFAULT_IDENTIFIERS: LazyLock<HashMap<char, char>> = LazyLock::new(|| {
1467 let mut identifiers = HashMap::with_capacity(4);
1468 identifiers.insert('"', '"');
1469 identifiers.insert('`', '`');
1470 identifiers
1473});
1474
1475static DEFAULT_COMMENTS: LazyLock<HashMap<String, Option<String>>> = LazyLock::new(|| {
1476 let mut comments = HashMap::with_capacity(4);
1477 comments.insert("--".to_string(), None);
1478 comments.insert("/*".to_string(), Some("*/".to_string()));
1479 comments
1480});
1481
1482#[derive(Debug, Clone)]
1484pub struct TokenizerConfig {
1485 pub keywords: HashMap<String, TokenType>,
1487 pub single_tokens: HashMap<char, TokenType>,
1489 pub quotes: HashMap<String, String>,
1491 pub identifiers: HashMap<char, char>,
1493 pub identifier_backslash_escapes: bool,
1495 pub comments: HashMap<String, Option<String>>,
1497 pub string_escapes: Vec<char>,
1499 pub nested_comments: bool,
1501 pub escape_follow_chars: Vec<char>,
1506 pub b_prefix_is_byte_string: bool,
1509 pub numeric_literals: HashMap<String, String>,
1512 pub identifiers_can_start_with_digit: bool,
1516 pub hex_number_strings: bool,
1520 pub hex_string_is_integer_type: bool,
1524 pub allow_empty_hex_string: bool,
1527 pub string_escapes_allowed_in_raw_strings: bool,
1532 pub hash_comments: bool,
1534 pub dollar_sign_is_identifier: bool,
1538 pub insert_format_raw_data: bool,
1542 pub numbers_can_be_underscore_separated: bool,
1546 pub recover_terminal_backslash_quote: bool,
1550 pub recover_unterminated_string: bool,
1554 pub double_slash_int_div: bool,
1556}
1557
1558impl Default for TokenizerConfig {
1559 fn default() -> Self {
1560 Self {
1561 keywords: DEFAULT_KEYWORDS.clone(),
1562 single_tokens: DEFAULT_SINGLE_TOKENS.clone(),
1563 quotes: DEFAULT_QUOTES.clone(),
1564 identifiers: DEFAULT_IDENTIFIERS.clone(),
1565 identifier_backslash_escapes: false,
1566 comments: DEFAULT_COMMENTS.clone(),
1567 string_escapes: vec!['\''],
1570 nested_comments: true,
1571 escape_follow_chars: vec![],
1573 b_prefix_is_byte_string: false,
1575 numeric_literals: HashMap::new(),
1576 identifiers_can_start_with_digit: false,
1577 hex_number_strings: false,
1578 hex_string_is_integer_type: false,
1579 allow_empty_hex_string: false,
1580 string_escapes_allowed_in_raw_strings: true,
1583 hash_comments: false,
1584 dollar_sign_is_identifier: false,
1585 insert_format_raw_data: false,
1586 numbers_can_be_underscore_separated: false,
1587 recover_terminal_backslash_quote: false,
1588 recover_unterminated_string: false,
1589 double_slash_int_div: false,
1590 }
1591 }
1592}
1593
1594pub struct Tokenizer {
1596 config: Arc<TokenizerConfig>,
1597}
1598
1599impl Tokenizer {
1600 pub fn new(config: TokenizerConfig) -> Self {
1602 Self {
1603 config: Arc::new(config),
1604 }
1605 }
1606
1607 pub(crate) fn from_shared_config(config: Arc<TokenizerConfig>) -> Self {
1608 Self { config }
1609 }
1610
1611 pub fn default_config() -> Self {
1613 Self::new(TokenizerConfig::default())
1614 }
1615
1616 pub fn tokenize(&self, sql: &str) -> Result<Vec<Token>> {
1618 if sql.is_ascii() {
1619 TokenizerState::<_, Token>::new(sql, &self.config, AsciiCursor(sql.as_bytes()))
1620 .tokenize()
1621 } else {
1622 TokenizerState::<_, Token>::new(sql, &self.config, UnicodeCursor::new(sql)).tokenize()
1623 }
1624 }
1625
1626 pub(crate) fn tokenize_for_parser(
1627 &self,
1628 sql: &Arc<str>,
1629 ) -> Result<(Vec<ParserToken>, TokenGuardStats)> {
1630 if sql.is_ascii() {
1631 let mut state = TokenizerState::<_, ParserToken>::new_shared(
1632 sql,
1633 Arc::clone(sql),
1634 &self.config,
1635 AsciiCursor(sql.as_bytes()),
1636 );
1637 let tokens = state.tokenize()?;
1638 Ok((tokens, state.guard_stats.take().unwrap_or_default()))
1639 } else {
1640 let mut state = TokenizerState::<_, ParserToken>::new_shared(
1641 sql,
1642 Arc::clone(sql),
1643 &self.config,
1644 UnicodeCursor::new(sql),
1645 );
1646 let tokens = state.tokenize()?;
1647 Ok((tokens, state.guard_stats.take().unwrap_or_default()))
1648 }
1649 }
1650
1651 #[cfg(test)]
1652 fn tokenize_without_ascii_fast_path(&self, sql: &str) -> Result<Vec<Token>> {
1653 TokenizerState::new(sql, &self.config, UnicodeCursor::new(sql)).tokenize()
1654 }
1655
1656 #[cfg(test)]
1657 pub(crate) fn shares_config_with(&self, other: &Self) -> bool {
1658 Arc::ptr_eq(&self.config, &other.config)
1659 }
1660}
1661
1662impl Default for Tokenizer {
1663 fn default() -> Self {
1664 Self::default_config()
1665 }
1666}
1667
1668trait TokenizerCursor {
1669 fn len(&self) -> usize;
1670 fn char_at(&self, index: usize) -> char;
1671 fn text_from_range(&self, source: &str, start: usize, end: usize) -> String;
1672
1673 fn source_range<'a>(&self, _source: &'a str, _start: usize, _end: usize) -> Option<&'a str> {
1674 None
1675 }
1676
1677 fn range_contains(&self, start: usize, needle: char) -> bool {
1678 (start..self.len()).any(|index| self.char_at(index) == needle)
1679 }
1680}
1681
1682struct AsciiCursor<'a>(&'a [u8]);
1683
1684impl TokenizerCursor for AsciiCursor<'_> {
1685 #[inline]
1686 fn len(&self) -> usize {
1687 self.0.len()
1688 }
1689
1690 #[inline]
1691 fn char_at(&self, index: usize) -> char {
1692 self.0[index] as char
1693 }
1694
1695 #[inline]
1696 fn text_from_range(&self, source: &str, start: usize, end: usize) -> String {
1697 source[start..end].to_string()
1698 }
1699
1700 #[inline]
1701 fn source_range<'a>(&self, source: &'a str, start: usize, end: usize) -> Option<&'a str> {
1702 Some(&source[start..end])
1703 }
1704}
1705
1706struct UnicodeCursor(Vec<char>);
1707
1708impl UnicodeCursor {
1709 fn new(source: &str) -> Self {
1710 Self(source.chars().collect())
1711 }
1712}
1713
1714impl TokenizerCursor for UnicodeCursor {
1715 #[inline]
1716 fn len(&self) -> usize {
1717 self.0.len()
1718 }
1719
1720 #[inline]
1721 fn char_at(&self, index: usize) -> char {
1722 self.0[index]
1723 }
1724
1725 #[inline]
1726 fn text_from_range(&self, _source: &str, start: usize, end: usize) -> String {
1727 self.0[start..end].iter().collect()
1728 }
1729}
1730
1731struct TokenizerState<'a, C, T> {
1733 source: &'a str,
1734 shared_source: Option<Arc<str>>,
1735 cursor: C,
1736 size: usize,
1737 tokens: Vec<T>,
1738 start: usize,
1739 current: usize,
1740 line: usize,
1741 column: usize,
1742 comments: Vec<String>,
1743 guard_stats: Option<TokenGuardStats>,
1744 config: &'a TokenizerConfig,
1745}
1746
1747impl<'a, C: TokenizerCursor, T: TokenOutput> TokenizerState<'a, C, T> {
1748 fn new(sql: &'a str, config: &'a TokenizerConfig, cursor: C) -> Self {
1749 let size = cursor.len();
1750 Self {
1751 source: sql,
1752 shared_source: None,
1753 cursor,
1754 size,
1755 tokens: Vec::new(),
1756 start: 0,
1757 current: 0,
1758 line: 1,
1759 column: 1,
1760 comments: Vec::new(),
1761 guard_stats: None,
1762 config,
1763 }
1764 }
1765
1766 fn new_shared(sql: &'a str, source: Arc<str>, config: &'a TokenizerConfig, cursor: C) -> Self {
1767 let size = cursor.len();
1768 Self {
1769 source: sql,
1770 shared_source: Some(source),
1771 cursor,
1772 size,
1773 tokens: Vec::new(),
1774 start: 0,
1775 current: 0,
1776 line: 1,
1777 column: 1,
1778 comments: Vec::new(),
1779 guard_stats: Some(TokenGuardStats::default()),
1780 config,
1781 }
1782 }
1783
1784 fn tokenize(&mut self) -> Result<Vec<T>> {
1785 while !self.is_at_end() {
1786 self.skip_whitespace();
1787 if self.is_at_end() {
1788 break;
1789 }
1790
1791 self.start = self.current;
1792 self.scan_token()?;
1793
1794 if self.config.insert_format_raw_data {
1797 if let Some(raw) = self.try_scan_insert_format_raw_data() {
1798 if !raw.is_empty() {
1799 self.start = self.current;
1800 self.add_token_with_text(TokenType::Var, raw);
1801 }
1802 }
1803 }
1804 }
1805
1806 if !self.comments.is_empty() {
1811 if let Some(last) = self.tokens.last_mut() {
1812 last.trailing_comments_mut().extend(self.comments.drain(..));
1813 }
1814 }
1815
1816 Ok(std::mem::take(&mut self.tokens))
1817 }
1818
1819 #[inline]
1820 fn is_at_end(&self) -> bool {
1821 self.current >= self.size
1822 }
1823
1824 #[inline]
1825 fn text_from_range(&self, start: usize, end: usize) -> String {
1826 self.cursor.text_from_range(self.source, start, end)
1827 }
1828
1829 #[inline]
1830 fn char_at(&self, index: usize) -> char {
1831 self.cursor.char_at(index)
1832 }
1833
1834 #[inline]
1835 fn range_contains(&self, start: usize, needle: char) -> bool {
1836 self.cursor.range_contains(start, needle)
1837 }
1838
1839 #[inline]
1840 fn peek(&self) -> char {
1841 if self.is_at_end() {
1842 '\0'
1843 } else {
1844 self.char_at(self.current)
1845 }
1846 }
1847
1848 #[inline]
1849 fn peek_next(&self) -> char {
1850 if self.current + 1 >= self.size {
1851 '\0'
1852 } else {
1853 self.char_at(self.current + 1)
1854 }
1855 }
1856
1857 #[inline]
1858 fn advance(&mut self) -> char {
1859 let c = self.peek();
1860 self.current += 1;
1861 if c == '\n' {
1862 self.line += 1;
1863 self.column = 1;
1864 } else {
1865 self.column += 1;
1866 }
1867 c
1868 }
1869
1870 #[inline]
1871 fn advance_ascii_to(&mut self, end: usize) -> bool {
1872 let Some(text) = self.cursor.source_range(self.source, self.current, end) else {
1873 return false;
1874 };
1875
1876 let newline_count = text
1877 .as_bytes()
1878 .iter()
1879 .filter(|&&byte| byte == b'\n')
1880 .count();
1881 if newline_count == 0 {
1882 self.column += end - self.current;
1883 } else {
1884 self.line += newline_count;
1885 let last_newline = text
1886 .as_bytes()
1887 .iter()
1888 .rposition(|&byte| byte == b'\n')
1889 .expect("newline count is non-zero");
1890 self.column = text.len() - last_newline;
1891 }
1892 self.current = end;
1893 true
1894 }
1895
1896 #[inline]
1897 fn advance_ascii_digits(&mut self) -> bool {
1898 let Some(rest) = self
1899 .cursor
1900 .source_range(self.source, self.current, self.size)
1901 else {
1902 return false;
1903 };
1904 let bytes = rest.as_bytes();
1905 let mut length = 0;
1906 while length < bytes.len() {
1907 match bytes[length] {
1908 b'0'..=b'9' => length += 1,
1909 b'_' if bytes.get(length + 1).is_some_and(u8::is_ascii_digit) => length += 1,
1910 _ => break,
1911 }
1912 }
1913 self.current += length;
1914 self.column += length;
1915 true
1916 }
1917
1918 #[inline]
1919 fn advance_ascii_hex_digits(&mut self) -> bool {
1920 let Some(rest) = self
1921 .cursor
1922 .source_range(self.source, self.current, self.size)
1923 else {
1924 return false;
1925 };
1926 let bytes = rest.as_bytes();
1927 let mut length = 0;
1928 while length < bytes.len() {
1929 match bytes[length] {
1930 byte if byte.is_ascii_hexdigit() => length += 1,
1931 b'_' if bytes.get(length + 1).is_some_and(u8::is_ascii_hexdigit) => length += 1,
1932 _ => break,
1933 }
1934 }
1935 self.current += length;
1936 self.column += length;
1937 true
1938 }
1939
1940 #[inline]
1941 fn advance_ascii_identifier(&mut self) -> bool {
1942 let Some(rest) = self
1943 .cursor
1944 .source_range(self.source, self.current, self.size)
1945 else {
1946 return false;
1947 };
1948 let bytes = rest.as_bytes();
1949 let mut length = 0;
1950 while length < bytes.len() {
1951 let byte = bytes[length];
1952 if byte == b'#' && matches!(bytes.get(length + 1), Some(b'>') | Some(b'-')) {
1953 break;
1954 }
1955 if byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'$' | b'#' | b'@') {
1956 length += 1;
1957 } else {
1958 break;
1959 }
1960 }
1961 self.current += length;
1962 self.column += length;
1963 true
1964 }
1965
1966 fn try_scan_simple_quoted_content(
1967 &mut self,
1968 quote: char,
1969 backslash_is_escape: bool,
1970 ) -> Option<(usize, usize)> {
1971 let content_start = self.current;
1972 let rest = self
1973 .cursor
1974 .source_range(self.source, content_start, self.size)?;
1975 let quote_offset = rest.find(quote)?;
1976 let content_end = content_start + quote_offset;
1977
1978 if (content_end + 1 < self.size && self.char_at(content_end + 1) == quote)
1979 || (backslash_is_escape && rest[..quote_offset].contains('\\'))
1980 {
1981 return None;
1982 }
1983
1984 self.advance_ascii_to(content_end);
1985 self.advance();
1986 Some((content_start, content_end))
1987 }
1988
1989 fn skip_whitespace(&mut self) {
1990 let mut saw_newline = false;
1995 while !self.is_at_end() {
1996 let c = self.peek();
1997 match c {
1998 ' ' | '\t' | '\r' => {
1999 self.advance();
2000 }
2001 '\n' => {
2002 saw_newline = true;
2003 self.advance();
2004 }
2005 '\u{00A0}' | '\u{2000}'..='\u{200B}' | '\u{3000}' | '\u{FEFF}' => {
2010 self.advance();
2011 }
2012 '-' if self.peek_next() == '-' => {
2013 self.scan_line_comment(saw_newline);
2014 saw_newline = true;
2016 }
2017 '/' if self.peek_next() == '/' && self.config.hash_comments => {
2018 self.scan_double_slash_comment();
2020 }
2021 '/' if self.peek_next() == '*' => {
2022 if self.current + 2 < self.size && self.char_at(self.current + 2) == '+' {
2024 break;
2026 }
2027 if self.scan_block_comment(saw_newline).is_err() {
2028 return;
2029 }
2030 }
2032 '/' if self.peek_next() == '/' && self.config.comments.contains_key("//") => {
2033 let prev_non_ws = if self.current > 0 {
2037 let mut i = self.current - 1;
2038 while i > 0 && (self.char_at(i) == ' ' || self.char_at(i) == '\t') {
2039 i -= 1;
2040 }
2041 self.char_at(i)
2042 } else {
2043 '\0'
2044 };
2045 if prev_non_ws == ':' || prev_non_ws == '/' {
2046 break;
2048 }
2049 self.scan_line_comment(saw_newline);
2050 saw_newline = true;
2052 }
2053 '#' if self.config.hash_comments => {
2054 self.scan_hash_line_comment();
2055 }
2056 _ => break,
2057 }
2058 }
2059 }
2060
2061 fn scan_hash_line_comment(&mut self) {
2062 self.advance(); let start = self.current;
2064 while !self.is_at_end() && self.peek() != '\n' {
2065 self.advance();
2066 }
2067 let comment = self.text_from_range(start, self.current);
2068 let comment_text = comment.trim().to_string();
2069 if let Some(last) = self.tokens.last_mut() {
2070 last.trailing_comments_mut().push(comment_text);
2071 } else {
2072 self.comments.push(comment_text);
2073 }
2074 }
2075
2076 fn scan_double_slash_comment(&mut self) {
2077 self.advance(); self.advance(); let start = self.current;
2080 while !self.is_at_end() && self.peek() != '\n' {
2081 self.advance();
2082 }
2083 let comment = self.text_from_range(start, self.current);
2084 let comment_text = comment.trim().to_string();
2085 if let Some(last) = self.tokens.last_mut() {
2086 last.trailing_comments_mut().push(comment_text);
2087 } else {
2088 self.comments.push(comment_text);
2089 }
2090 }
2091
2092 fn scan_line_comment(&mut self, after_newline: bool) {
2093 self.advance(); self.advance(); let start = self.current;
2096 while !self.is_at_end() && self.peek() != '\n' {
2097 self.advance();
2098 }
2099 let comment_text = self.text_from_range(start, self.current);
2100
2101 if after_newline || self.tokens.is_empty() {
2104 self.comments.push(comment_text);
2105 } else if let Some(last) = self.tokens.last_mut() {
2106 last.trailing_comments_mut().push(comment_text);
2107 }
2108 }
2109
2110 fn scan_block_comment(&mut self, after_newline: bool) -> Result<()> {
2111 self.advance(); self.advance(); let content_start = self.current;
2114 let mut depth = 1;
2115
2116 while !self.is_at_end() && depth > 0 {
2117 if self.peek() == '/' && self.peek_next() == '*' && self.config.nested_comments {
2118 self.advance();
2119 self.advance();
2120 depth += 1;
2121 } else if self.peek() == '*' && self.peek_next() == '/' {
2122 depth -= 1;
2123 if depth > 0 {
2124 self.advance();
2125 self.advance();
2126 }
2127 } else {
2128 self.advance();
2129 }
2130 }
2131
2132 if depth > 0 {
2133 return Err(Error::tokenize(
2134 "Unterminated block comment",
2135 self.line,
2136 self.column,
2137 self.start,
2138 self.current,
2139 ));
2140 }
2141
2142 let content = self.text_from_range(content_start, self.current);
2144 self.advance(); self.advance(); let comment_text = format!("/*{}*/", content);
2149
2150 if after_newline || self.tokens.is_empty() {
2153 self.comments.push(comment_text);
2154 } else if let Some(last) = self.tokens.last_mut() {
2155 last.trailing_comments_mut().push(comment_text);
2156 }
2157
2158 Ok(())
2159 }
2160
2161 fn scan_hint(&mut self) -> Result<()> {
2163 self.advance(); self.advance(); self.advance(); let hint_start = self.current;
2167
2168 while !self.is_at_end() {
2170 if self.peek() == '*' && self.peek_next() == '/' {
2171 break;
2172 }
2173 self.advance();
2174 }
2175
2176 if self.is_at_end() {
2177 return Err(Error::tokenize(
2178 "Unterminated hint comment",
2179 self.line,
2180 self.column,
2181 self.start,
2182 self.current,
2183 ));
2184 }
2185
2186 let hint_text = self.text_from_range(hint_start, self.current);
2187 self.advance(); self.advance(); self.add_token_with_text(TokenType::Hint, hint_text.trim().to_string());
2191
2192 Ok(())
2193 }
2194
2195 fn scan_positional_parameter(&mut self) -> Result<()> {
2197 self.advance(); let start = self.current;
2199
2200 while !self.is_at_end() && self.peek().is_ascii_digit() {
2201 self.advance();
2202 }
2203
2204 let number = self.text_from_range(start, self.current);
2205 self.add_token_with_text(TokenType::Parameter, number);
2206 Ok(())
2207 }
2208
2209 fn try_scan_tagged_dollar_string(&mut self) -> Result<Option<()>> {
2214 let saved_pos = self.current;
2215
2216 self.advance(); let tag_start = self.current;
2222 while !self.is_at_end()
2223 && (self.peek().is_alphanumeric() || self.peek() == '_' || !self.peek().is_ascii())
2224 {
2225 self.advance();
2226 }
2227 let tag = self.text_from_range(tag_start, self.current);
2228
2229 if self.is_at_end() || self.peek() != '$' {
2231 self.current = saved_pos;
2233 return Ok(None);
2234 }
2235 self.advance(); let content_start = self.current;
2239 let closing_tag = format!("${}$", tag);
2240 let closing_chars: Vec<char> = closing_tag.chars().collect();
2241
2242 loop {
2243 if self.is_at_end() {
2244 self.current = saved_pos;
2246 return Ok(None);
2247 }
2248
2249 if self.peek() == '$' && self.current + closing_chars.len() <= self.size {
2251 let matches = closing_chars.iter().enumerate().all(|(j, &ch)| {
2252 self.current + j < self.size && self.char_at(self.current + j) == ch
2253 });
2254 if matches {
2255 let content = self.text_from_range(content_start, self.current);
2256 for _ in 0..closing_chars.len() {
2258 self.advance();
2259 }
2260 let token_text = format!("{}\x00{}", tag, content);
2262 self.add_token_with_text(TokenType::DollarString, token_text);
2263 return Ok(Some(()));
2264 }
2265 }
2266 self.advance();
2267 }
2268 }
2269
2270 fn scan_dollar_quoted_string(&mut self) -> Result<()> {
2275 self.advance(); self.advance(); let start = self.current;
2280 while !self.is_at_end() {
2281 if self.peek() == '$'
2282 && self.current + 1 < self.size
2283 && self.char_at(self.current + 1) == '$'
2284 {
2285 break;
2286 }
2287 self.advance();
2288 }
2289
2290 let content = self.text_from_range(start, self.current);
2291
2292 if !self.is_at_end() {
2293 self.advance(); self.advance(); }
2296
2297 self.add_token_with_text(TokenType::DollarString, content);
2298 Ok(())
2299 }
2300
2301 fn scan_token(&mut self) -> Result<()> {
2302 let c = self.peek();
2303
2304 if c == '\'' {
2306 if self.config.quotes.contains_key("'''")
2308 && self.peek_next() == '\''
2309 && self.current + 2 < self.size
2310 && self.char_at(self.current + 2) == '\''
2311 {
2312 return self.scan_triple_quoted_string('\'');
2313 }
2314 return self.scan_string();
2315 }
2316
2317 if c == '"'
2319 && self.config.quotes.contains_key("\"\"\"")
2320 && self.peek_next() == '"'
2321 && self.current + 2 < self.size
2322 && self.char_at(self.current + 2) == '"'
2323 {
2324 return self.scan_triple_quoted_string('"');
2325 }
2326
2327 if c == '"'
2330 && self.config.quotes.contains_key("\"")
2331 && !self.config.identifiers.contains_key(&'"')
2332 {
2333 return self.scan_double_quoted_string();
2334 }
2335
2336 if let Some(&end_quote) = self.config.identifiers.get(&c) {
2338 return self.scan_quoted_identifier(end_quote);
2339 }
2340
2341 if c.is_ascii_digit() {
2343 return self.scan_number();
2344 }
2345
2346 if c == '.' && self.peek_next().is_ascii_digit() {
2353 let prev_char = if self.current > 0 {
2354 self.char_at(self.current - 1)
2355 } else {
2356 '\0'
2357 };
2358 let is_after_ident = prev_char.is_alphanumeric()
2359 || prev_char == '_'
2360 || prev_char == '`'
2361 || prev_char == '"'
2362 || prev_char == ']'
2363 || prev_char == ')';
2364 if prev_char != '.' && !is_after_ident {
2365 return self.scan_number_starting_with_dot();
2366 }
2367 }
2368
2369 if c == '/'
2371 && self.peek_next() == '*'
2372 && self.current + 2 < self.size
2373 && self.char_at(self.current + 2) == '+'
2374 {
2375 return self.scan_hint();
2376 }
2377
2378 if let Some(token_type) = self.try_scan_multi_char_operator() {
2380 self.add_token(token_type);
2381 return Ok(());
2382 }
2383
2384 if c == '$'
2387 && (self.peek_next().is_alphanumeric()
2388 || self.peek_next() == '_'
2389 || !self.peek_next().is_ascii())
2390 {
2391 if let Some(()) = self.try_scan_tagged_dollar_string()? {
2392 return Ok(());
2393 }
2394 if self.config.dollar_sign_is_identifier {
2397 return self.scan_dollar_identifier();
2398 }
2399 }
2400
2401 if c == '$' && self.peek_next() == '$' {
2403 return self.scan_dollar_quoted_string();
2404 }
2405
2406 if c == '$' && self.peek_next().is_ascii_digit() {
2408 return self.scan_positional_parameter();
2409 }
2410
2411 if c == '$' && self.config.dollar_sign_is_identifier {
2413 return self.scan_dollar_identifier();
2414 }
2415
2416 if (c == '#' || c == '@')
2419 && (self.peek_next().is_alphanumeric()
2420 || self.peek_next() == '_'
2421 || self.peek_next() == '#')
2422 {
2423 return self.scan_tsql_identifier();
2424 }
2425
2426 if let Some(&token_type) = self.config.single_tokens.get(&c) {
2428 self.advance();
2429 self.add_token(token_type);
2430 return Ok(());
2431 }
2432
2433 if c == '\u{2212}' {
2435 self.advance();
2436 self.add_token(TokenType::Dash);
2437 return Ok(());
2438 }
2439
2440 if c == '\u{2044}' {
2442 self.advance();
2443 self.add_token(TokenType::Slash);
2444 return Ok(());
2445 }
2446
2447 if c == '\u{2018}' || c == '\u{2019}' {
2449 return self.scan_unicode_quoted_string(c);
2451 }
2452 if c == '\u{201C}' || c == '\u{201D}' {
2453 return self.scan_unicode_quoted_identifier(c);
2455 }
2456
2457 self.scan_identifier_or_keyword()
2459 }
2460
2461 fn try_scan_multi_char_operator(&mut self) -> Option<TokenType> {
2462 let c = self.peek();
2463 let next = self.peek_next();
2464 let third = if self.current + 2 < self.size {
2465 self.char_at(self.current + 2)
2466 } else {
2467 '\0'
2468 };
2469
2470 if c == '-' && next == '|' && third == '-' {
2473 self.advance();
2474 self.advance();
2475 self.advance();
2476 return Some(TokenType::Adjacent);
2477 }
2478
2479 if c == '|' && next == '|' && third == '/' {
2481 self.advance();
2482 self.advance();
2483 self.advance();
2484 return Some(TokenType::DPipeSlash);
2485 }
2486
2487 if c == '#' && next == '>' && third == '>' {
2489 self.advance();
2490 self.advance();
2491 self.advance();
2492 return Some(TokenType::DHashArrow);
2493 }
2494
2495 if c == '-' && next == '>' && third == '>' {
2497 self.advance();
2498 self.advance();
2499 self.advance();
2500 return Some(TokenType::DArrow);
2501 }
2502
2503 if c == '<' && next == '=' && third == '>' {
2505 self.advance();
2506 self.advance();
2507 self.advance();
2508 return Some(TokenType::NullsafeEq);
2509 }
2510
2511 if c == '<' && next == '-' && third == '>' {
2513 self.advance();
2514 self.advance();
2515 self.advance();
2516 return Some(TokenType::LrArrow);
2517 }
2518
2519 if c == '<' && next == '@' {
2521 self.advance();
2522 self.advance();
2523 return Some(TokenType::LtAt);
2524 }
2525
2526 if c == '@' && next == '>' {
2528 self.advance();
2529 self.advance();
2530 return Some(TokenType::AtGt);
2531 }
2532
2533 if c == '~' && next == '~' && third == '~' {
2535 self.advance();
2536 self.advance();
2537 self.advance();
2538 return Some(TokenType::Glob);
2539 }
2540
2541 if c == '~' && next == '~' && third == '*' {
2543 self.advance();
2544 self.advance();
2545 self.advance();
2546 return Some(TokenType::ILike);
2547 }
2548
2549 let fourth = if self.current + 3 < self.size {
2551 self.char_at(self.current + 3)
2552 } else {
2553 '\0'
2554 };
2555 if c == '!' && next == '~' && third == '~' && fourth == '*' {
2556 self.advance();
2557 self.advance();
2558 self.advance();
2559 self.advance();
2560 return Some(TokenType::NotILike);
2561 }
2562
2563 if c == '!' && next == '~' && third == '~' {
2565 self.advance();
2566 self.advance();
2567 self.advance();
2568 return Some(TokenType::NotLike);
2569 }
2570
2571 if c == '!' && next == '~' && third == '*' {
2573 self.advance();
2574 self.advance();
2575 self.advance();
2576 return Some(TokenType::NotIRLike);
2577 }
2578
2579 if c == '!' && next == ':' && third == '>' {
2581 self.advance();
2582 self.advance();
2583 self.advance();
2584 return Some(TokenType::NColonGt);
2585 }
2586
2587 if c == '?' && next == ':' && third == ':' {
2589 self.advance();
2590 self.advance();
2591 self.advance();
2592 return Some(TokenType::QDColon);
2593 }
2594
2595 if c == '!' && next == '~' {
2597 self.advance();
2598 self.advance();
2599 return Some(TokenType::NotRLike);
2600 }
2601
2602 if c == '~' && next == '~' {
2604 self.advance();
2605 self.advance();
2606 return Some(TokenType::Like);
2607 }
2608
2609 if c == '~' && next == '*' {
2611 self.advance();
2612 self.advance();
2613 return Some(TokenType::IRLike);
2614 }
2615
2616 if c == ':' && next == ':' && third == '$' {
2619 self.advance();
2620 self.advance();
2621 self.advance();
2622 return Some(TokenType::DColonDollar);
2623 }
2624 if c == ':' && next == ':' && third == '%' {
2625 self.advance();
2626 self.advance();
2627 self.advance();
2628 return Some(TokenType::DColonPercent);
2629 }
2630 if c == ':' && next == ':' && third == '?' {
2631 self.advance();
2632 self.advance();
2633 self.advance();
2634 return Some(TokenType::DColonQMark);
2635 }
2636
2637 let token_type = match (c, next) {
2639 ('.', ':') => Some(TokenType::DotColon),
2640 ('=', '=') => Some(TokenType::Eq), ('<', '=') => Some(TokenType::Lte),
2642 ('>', '=') => Some(TokenType::Gte),
2643 ('!', '=') => Some(TokenType::Neq),
2644 ('<', '>') => Some(TokenType::Neq),
2645 ('^', '=') => Some(TokenType::Neq),
2646 ('<', '<') => Some(TokenType::LtLt),
2647 ('>', '>') => Some(TokenType::GtGt),
2648 ('|', '|') => Some(TokenType::DPipe),
2649 ('|', '/') => Some(TokenType::PipeSlash), ('/', '/') if self.config.double_slash_int_div => Some(TokenType::Div), (':', ':') => Some(TokenType::DColon),
2652 (':', '=') => Some(TokenType::ColonEq), (':', '>') => Some(TokenType::ColonGt), ('-', '>') => Some(TokenType::Arrow), ('=', '>') => Some(TokenType::FArrow), ('&', '&') => Some(TokenType::DAmp),
2657 ('&', '<') => 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,
2670 };
2671
2672 if token_type.is_some() {
2673 self.advance();
2674 self.advance();
2675 }
2676
2677 token_type
2678 }
2679
2680 fn scan_string(&mut self) -> Result<()> {
2681 self.advance(); if let Some((text_start, text_end)) =
2683 self.try_scan_simple_quoted_content('\'', self.config.string_escapes.contains(&'\\'))
2684 {
2685 self.add_token_from_source(TokenType::String, text_start, text_end);
2686 return Ok(());
2687 }
2688 let mut value = String::new();
2689
2690 while !self.is_at_end() {
2691 let c = self.peek();
2692 if c == '\'' {
2693 if self.peek_next() == '\'' {
2694 value.push('\'');
2696 self.advance();
2697 self.advance();
2698 } else {
2699 break;
2700 }
2701 } else if c == '\\' && self.config.string_escapes.contains(&'\\') {
2702 if self.config.recover_terminal_backslash_quote
2703 && self.peek_next() == '\''
2704 && !self.range_contains(self.current + 2, '\'')
2705 {
2706 value.push(self.advance());
2707 break;
2708 }
2709
2710 self.scan_backslash_escape(&mut value);
2711 } else {
2712 value.push(self.advance());
2713 }
2714 }
2715
2716 if self.is_at_end() {
2717 if self.config.recover_unterminated_string {
2718 self.add_token_with_text(TokenType::String, value);
2719 return Ok(());
2720 }
2721
2722 return Err(Error::tokenize(
2723 "Unterminated string",
2724 self.line,
2725 self.column,
2726 self.start,
2727 self.current,
2728 ));
2729 }
2730
2731 self.advance(); self.add_token_with_text(TokenType::String, value);
2733 Ok(())
2734 }
2735
2736 fn scan_double_quoted_string(&mut self) -> Result<()> {
2738 self.advance(); let mut value = String::new();
2740
2741 while !self.is_at_end() {
2742 let c = self.peek();
2743 if c == '"' {
2744 if self.peek_next() == '"' {
2745 value.push('"');
2747 self.advance();
2748 self.advance();
2749 } else {
2750 break;
2751 }
2752 } else if c == '\\' && self.config.string_escapes.contains(&'\\') {
2753 self.scan_backslash_escape(&mut value);
2754 } else {
2755 value.push(self.advance());
2756 }
2757 }
2758
2759 if self.is_at_end() {
2760 return Err(Error::tokenize(
2761 "Unterminated double-quoted string",
2762 self.line,
2763 self.column,
2764 self.start,
2765 self.current,
2766 ));
2767 }
2768
2769 self.advance(); self.add_token_with_text(TokenType::String, value);
2771 Ok(())
2772 }
2773
2774 fn scan_backslash_escape(&mut self, value: &mut String) {
2775 self.advance(); if self.is_at_end() {
2777 value.push('\\');
2778 return;
2779 }
2780
2781 let escaped = self.advance();
2782 let restricted = !self.config.escape_follow_chars.is_empty();
2783 let always_allowed = matches!(escaped, '\\' | '\'' | '"');
2784 if restricted && !always_allowed && !self.config.escape_follow_chars.contains(&escaped) {
2785 value.push(escaped);
2786 return;
2787 }
2788
2789 let supports_octal =
2790 restricted && ('1'..='7').any(|digit| self.config.escape_follow_chars.contains(&digit));
2791 if supports_octal && escaped.is_digit(8) {
2792 if let Some(codepoint) = self.peek_radix_digits(2, 8).and_then(|suffix| {
2793 let first = escaped.to_digit(8)?;
2794 first.checked_mul(64)?.checked_add(suffix)
2795 }) {
2796 if let Ok(byte) = u8::try_from(codepoint) {
2797 self.advance_count(2);
2798 value.push(byte as char);
2799 return;
2800 }
2801 }
2802
2803 if escaped == '0' {
2804 value.push('\0');
2805 } else {
2806 value.push(escaped);
2807 }
2808 return;
2809 }
2810
2811 match escaped {
2812 'n' => value.push('\n'),
2813 'r' => value.push('\r'),
2814 't' => value.push('\t'),
2815 '0' => value.push('\0'),
2816 'Z' => value.push('\x1A'),
2817 'a' => value.push('\x07'),
2818 'b' => value.push('\x08'),
2819 'f' => value.push('\x0C'),
2820 'v' => value.push('\x0B'),
2821 'x' => {
2822 if let Some(codepoint) = self.peek_radix_digits(2, 16) {
2823 self.advance_count(2);
2824 value.push(codepoint as u8 as char);
2825 } else if restricted {
2826 value.push('x');
2828 } else {
2829 value.push('\\');
2832 value.push('x');
2833 for _ in 0..2 {
2834 if !self.is_at_end() && self.peek().is_ascii_hexdigit() {
2835 value.push(self.advance());
2836 }
2837 }
2838 }
2839 }
2840 'u' if restricted && self.config.escape_follow_chars.contains(&'u') => {
2841 if let Some(codepoint) = self.peek_radix_digits(4, 16).and_then(char::from_u32) {
2842 self.advance_count(4);
2843 value.push(codepoint);
2844 } else {
2845 value.push('u');
2846 }
2847 }
2848 '\\' => value.push('\\'),
2849 '\'' => value.push('\''),
2850 '"' => value.push('"'),
2851 '%' => value.push('%'),
2852 '_' => value.push('_'),
2853 _ if restricted => value.push(escaped),
2854 _ => {
2855 value.push('\\');
2856 value.push(escaped);
2857 }
2858 }
2859 }
2860
2861 fn peek_radix_digits(&self, count: usize, radix: u32) -> Option<u32> {
2862 if self.current + count > self.size {
2863 return None;
2864 }
2865
2866 let mut value = 0_u32;
2867 for offset in 0..count {
2868 value = value
2869 .checked_mul(radix)?
2870 .checked_add(self.char_at(self.current + offset).to_digit(radix)?)?;
2871 }
2872 Some(value)
2873 }
2874
2875 fn advance_count(&mut self, count: usize) {
2876 for _ in 0..count {
2877 self.advance();
2878 }
2879 }
2880
2881 fn scan_triple_quoted_string(&mut self, quote_char: char) -> Result<()> {
2882 self.advance();
2884 self.advance();
2885 self.advance();
2886 let mut value = String::new();
2887
2888 while !self.is_at_end() {
2889 if self.peek() == quote_char
2891 && self.current + 1 < self.size
2892 && self.char_at(self.current + 1) == quote_char
2893 && self.current + 2 < self.size
2894 && self.char_at(self.current + 2) == quote_char
2895 {
2896 break;
2898 }
2899 if self.peek() == '\\' && self.config.string_escapes.contains(&'\\') {
2900 self.scan_backslash_escape(&mut value);
2901 } else {
2902 value.push(self.advance());
2903 }
2904 }
2905
2906 if self.is_at_end() {
2907 return Err(Error::tokenize(
2908 "Unterminated triple-quoted string",
2909 self.line,
2910 self.column,
2911 self.start,
2912 self.current,
2913 ));
2914 }
2915
2916 self.advance();
2918 self.advance();
2919 self.advance();
2920 let token_type = if quote_char == '"' {
2921 TokenType::TripleDoubleQuotedString
2922 } else {
2923 TokenType::TripleSingleQuotedString
2924 };
2925 self.add_token_with_text(token_type, value);
2926 Ok(())
2927 }
2928
2929 fn scan_quoted_identifier(&mut self, end_quote: char) -> Result<()> {
2930 self.advance(); let mut value = String::new();
2932
2933 loop {
2934 if self.is_at_end() {
2935 return Err(Error::tokenize(
2936 "Unterminated identifier",
2937 self.line,
2938 self.column,
2939 self.start,
2940 self.current,
2941 ));
2942 }
2943 if self.config.identifier_backslash_escapes && self.peek() == '\\' {
2944 if self.peek_next() == end_quote {
2945 value.push(end_quote);
2946 self.advance_count(2);
2947 } else {
2948 self.scan_backslash_escape(&mut value);
2949 }
2950 continue;
2951 }
2952 if self.peek() == end_quote {
2953 if self.peek_next() == end_quote {
2954 value.push(end_quote);
2956 self.advance(); self.advance(); } else {
2959 break;
2961 }
2962 } else {
2963 value.push(self.peek());
2964 self.advance();
2965 }
2966 }
2967
2968 self.advance(); self.add_token_with_text(TokenType::QuotedIdentifier, value);
2970 Ok(())
2971 }
2972
2973 fn scan_unicode_quoted_string(&mut self, open_quote: char) -> Result<()> {
2978 self.advance(); let start = self.current;
2980 let close_quote = if open_quote == '\u{2018}' {
2982 '\u{2019}' } else {
2984 '\u{2019}' };
2986 while !self.is_at_end() && self.peek() != close_quote {
2987 self.advance();
2988 }
2989 let value = self.text_from_range(start, self.current);
2990 if !self.is_at_end() {
2991 self.advance(); }
2993 self.add_token_with_text(TokenType::String, value);
2994 Ok(())
2995 }
2996
2997 fn scan_unicode_quoted_identifier(&mut self, open_quote: char) -> Result<()> {
3000 self.advance(); let start = self.current;
3002 let close_quote = if open_quote == '\u{201C}' {
3003 '\u{201D}' } else {
3005 '\u{201D}' };
3007 while !self.is_at_end() && self.peek() != close_quote && self.peek() != '"' {
3008 self.advance();
3009 }
3010 let value = self.text_from_range(start, self.current);
3011 if !self.is_at_end() {
3012 self.advance(); }
3014 self.add_token_with_text(TokenType::QuotedIdentifier, value);
3015 Ok(())
3016 }
3017
3018 fn scan_number(&mut self) -> Result<()> {
3019 if self.config.hex_number_strings && self.peek() == '0' && !self.is_at_end() {
3021 let next = if self.current + 1 < self.size {
3022 self.char_at(self.current + 1)
3023 } else {
3024 '\0'
3025 };
3026 if next == 'x' || next == 'X' {
3027 self.advance();
3029 self.advance();
3030 let hex_start = self.current;
3032 if !self.advance_ascii_hex_digits() {
3033 while !self.is_at_end()
3034 && (self.peek().is_ascii_hexdigit() || self.peek() == '_')
3035 {
3036 if self.peek() == '_' && !self.peek_next().is_ascii_hexdigit() {
3037 break;
3038 }
3039 self.advance();
3040 }
3041 }
3042 let has_hex_digits = self.current > hex_start;
3043 let next_is_identifier_part = {
3044 let next = self.peek();
3045 next.is_alphanumeric() || matches!(next, '_' | '$' | '#' | '@')
3046 };
3047 let is_empty_hex_string = self.config.allow_empty_hex_string
3048 && !self.config.hex_string_is_integer_type
3049 && !next_is_identifier_part;
3050 if has_hex_digits || is_empty_hex_string {
3051 let mut is_hex_float = false;
3053 if has_hex_digits && !self.is_at_end() && self.peek() == '.' {
3055 let after_dot = if self.current + 1 < self.size {
3056 self.char_at(self.current + 1)
3057 } else {
3058 '\0'
3059 };
3060 if after_dot.is_ascii_hexdigit() {
3061 is_hex_float = true;
3062 self.advance(); if !self.advance_ascii_hex_digits() {
3064 while !self.is_at_end() && self.peek().is_ascii_hexdigit() {
3065 self.advance();
3066 }
3067 }
3068 }
3069 }
3070 if has_hex_digits
3072 && !self.is_at_end()
3073 && (self.peek() == 'p' || self.peek() == 'P')
3074 {
3075 is_hex_float = true;
3076 self.advance(); if !self.is_at_end() && (self.peek() == '+' || self.peek() == '-') {
3078 self.advance();
3079 }
3080 if !self.advance_ascii_digits() {
3081 while !self.is_at_end() && self.peek().is_ascii_digit() {
3082 self.advance();
3083 }
3084 }
3085 }
3086 if is_hex_float {
3087 let raw_text = self.text_from_range(self.start, self.current);
3089 let full_text = if self.config.numbers_can_be_underscore_separated
3090 && raw_text.contains('_')
3091 {
3092 raw_text.replace('_', "")
3093 } else {
3094 raw_text
3095 };
3096 self.add_token_with_text(TokenType::Number, full_text);
3097 } else if self.config.hex_string_is_integer_type {
3098 let raw_value = self.text_from_range(hex_start, self.current);
3100 let hex_value = if self.config.numbers_can_be_underscore_separated
3101 && raw_value.contains('_')
3102 {
3103 raw_value.replace('_', "")
3104 } else {
3105 raw_value
3106 };
3107 self.add_token_with_text(TokenType::HexNumber, hex_value);
3108 } else {
3109 let raw_value = self.text_from_range(hex_start, self.current);
3111 let hex_value = if self.config.numbers_can_be_underscore_separated
3112 && raw_value.contains('_')
3113 {
3114 raw_value.replace('_', "")
3115 } else {
3116 raw_value
3117 };
3118 self.add_token_with_text(TokenType::HexString, hex_value);
3119 }
3120 return Ok(());
3121 }
3122 self.current = self.start + 1;
3125 }
3126 }
3127
3128 if !self.advance_ascii_digits() {
3130 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3131 if self.peek() == '_' && (self.is_at_end() || !self.peek_next().is_ascii_digit()) {
3133 break;
3134 }
3135 self.advance();
3136 }
3137 }
3138
3139 if self.peek() == '.' {
3143 let next = self.peek_next();
3144 if next != '.' {
3150 self.advance(); if !self.advance_ascii_digits() {
3153 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_')
3154 {
3155 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3156 break;
3157 }
3158 self.advance();
3159 }
3160 }
3161 }
3162 }
3163
3164 if self.peek() == 'e' || self.peek() == 'E' {
3166 self.advance();
3167 if self.peek() == '+' || self.peek() == '-' {
3168 self.advance();
3169 }
3170 if !self.advance_ascii_digits() {
3171 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3172 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3173 break;
3174 }
3175 self.advance();
3176 }
3177 }
3178 }
3179
3180 let source_text = self
3181 .cursor
3182 .source_range(self.source, self.start, self.current);
3183 let raw_owned = source_text
3184 .is_none()
3185 .then(|| self.text_from_range(self.start, self.current));
3186 let raw_text = source_text.unwrap_or_else(|| {
3187 raw_owned
3188 .as_deref()
3189 .expect("non-ASCII numbers own their text")
3190 });
3191 let normalized = (self.config.numbers_can_be_underscore_separated
3194 && raw_text.contains('_'))
3195 .then(|| raw_text.replace('_', ""));
3196 let text = normalized.as_deref().unwrap_or(raw_text);
3197
3198 if !self.config.numeric_literals.is_empty() && !self.is_at_end() {
3200 let next_char: String = self.peek().to_ascii_uppercase().to_string();
3201 let suffix_match = if self.current + 1 < self.size {
3203 let two_char: String = [
3204 self.char_at(self.current).to_ascii_uppercase(),
3205 self.char_at(self.current + 1).to_ascii_uppercase(),
3206 ]
3207 .iter()
3208 .collect();
3209 if self.config.numeric_literals.contains_key(&two_char) {
3210 let after_suffix = if self.current + 2 < self.size {
3212 self.char_at(self.current + 2)
3213 } else {
3214 ' '
3215 };
3216 if !after_suffix.is_alphanumeric() && after_suffix != '_' {
3217 Some((two_char, 2))
3218 } else {
3219 None
3220 }
3221 } else if self.config.numeric_literals.contains_key(&next_char) {
3222 let after_suffix = if self.current + 1 < self.size {
3224 self.char_at(self.current + 1)
3225 } else {
3226 ' '
3227 };
3228 if !after_suffix.is_alphanumeric() && after_suffix != '_' {
3229 Some((next_char, 1))
3230 } else {
3231 None
3232 }
3233 } else {
3234 None
3235 }
3236 } else if self.config.numeric_literals.contains_key(&next_char) {
3237 Some((next_char, 1))
3239 } else {
3240 None
3241 };
3242
3243 if let Some((suffix, len)) = suffix_match {
3244 for _ in 0..len {
3246 self.advance();
3247 }
3248 let type_name = self
3251 .config
3252 .numeric_literals
3253 .get(&suffix)
3254 .expect("suffix verified by contains_key above")
3255 .clone();
3256 let combined = format!("{}::{}", text, type_name);
3257 self.add_token_with_text(TokenType::Number, combined);
3258 return Ok(());
3259 }
3260 }
3261
3262 if self.config.identifiers_can_start_with_digit && !self.is_at_end() {
3265 let next = self.peek();
3266 if next.is_alphabetic() || next == '_' {
3267 if !self.advance_ascii_identifier() {
3269 while !self.is_at_end() {
3270 let ch = self.peek();
3271 if ch.is_alphanumeric() || ch == '_' {
3272 self.advance();
3273 } else {
3274 break;
3275 }
3276 }
3277 }
3278 self.add_token(TokenType::Identifier);
3279 return Ok(());
3280 }
3281 }
3282
3283 if let Some(text) = normalized.or(raw_owned) {
3284 self.add_token_with_text(TokenType::Number, text);
3285 } else {
3286 self.add_token(TokenType::Number);
3287 }
3288 Ok(())
3289 }
3290
3291 fn scan_number_starting_with_dot(&mut self) -> Result<()> {
3293 self.advance();
3295
3296 if !self.advance_ascii_digits() {
3298 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3299 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3300 break;
3301 }
3302 self.advance();
3303 }
3304 }
3305
3306 if self.peek() == 'e' || self.peek() == 'E' {
3308 self.advance();
3309 if self.peek() == '+' || self.peek() == '-' {
3310 self.advance();
3311 }
3312 if !self.advance_ascii_digits() {
3313 while !self.is_at_end() && (self.peek().is_ascii_digit() || self.peek() == '_') {
3314 if self.peek() == '_' && !self.peek_next().is_ascii_digit() {
3315 break;
3316 }
3317 self.advance();
3318 }
3319 }
3320 }
3321
3322 let source_text = self
3323 .cursor
3324 .source_range(self.source, self.start, self.current);
3325 let raw_owned = source_text
3326 .is_none()
3327 .then(|| self.text_from_range(self.start, self.current));
3328 let raw_text = source_text.unwrap_or_else(|| {
3329 raw_owned
3330 .as_deref()
3331 .expect("non-ASCII numbers own their text")
3332 });
3333 let normalized = (self.config.numbers_can_be_underscore_separated
3336 && raw_text.contains('_'))
3337 .then(|| raw_text.replace('_', ""));
3338 if let Some(text) = normalized.or(raw_owned) {
3339 self.add_token_with_text(TokenType::Number, text);
3340 } else {
3341 self.add_token(TokenType::Number);
3342 }
3343 Ok(())
3344 }
3345
3346 #[inline]
3349 fn lookup_keyword_ascii(keywords: &HashMap<String, TokenType>, text: &str) -> TokenType {
3350 if text.len() > 128 {
3351 return TokenType::Var;
3352 }
3353 let mut buf = [0u8; 128];
3354 for (i, b) in text.bytes().enumerate() {
3355 buf[i] = b.to_ascii_uppercase();
3356 }
3357 if let Ok(upper) = std::str::from_utf8(&buf[..text.len()]) {
3358 keywords.get(upper).copied().unwrap_or(TokenType::Var)
3359 } else {
3360 TokenType::Var
3361 }
3362 }
3363
3364 fn scan_identifier_or_keyword(&mut self) -> Result<()> {
3365 let first_char = self.peek();
3367 if !first_char.is_alphanumeric() && first_char != '_' {
3368 let c = self.advance();
3370 return Err(Error::tokenize(
3371 format!("Unexpected character: '{}'", c),
3372 self.line,
3373 self.column,
3374 self.start,
3375 self.current,
3376 ));
3377 }
3378
3379 if !self.advance_ascii_identifier() {
3380 while !self.is_at_end() {
3381 let c = self.peek();
3382 if c == '#' {
3386 let next_c = if self.current + 1 < self.size {
3387 self.char_at(self.current + 1)
3388 } else {
3389 '\0'
3390 };
3391 if next_c == '>' || next_c == '-' {
3392 break; }
3394 self.advance();
3395 } else if c.is_alphanumeric() || c == '_' || c == '$' || c == '@' {
3396 self.advance();
3397 } else {
3398 break;
3399 }
3400 }
3401 }
3402
3403 let source_text = self
3404 .cursor
3405 .source_range(self.source, self.start, self.current);
3406 let owned_text = source_text
3407 .is_none()
3408 .then(|| self.text_from_range(self.start, self.current));
3409 let text = source_text.unwrap_or_else(|| {
3410 owned_text
3411 .as_deref()
3412 .expect("non-ASCII identifiers own their text")
3413 });
3414
3415 if text.eq_ignore_ascii_case("NOT") && self.peek() == '=' {
3417 self.advance(); self.add_token(TokenType::Neq);
3419 return Ok(());
3420 }
3421
3422 let next_char = self.peek();
3425 let is_single_quote = next_char == '\'';
3426 let is_double_quote = next_char == '"' && self.config.quotes.contains_key("\"");
3427 let is_double_quote_for_raw = next_char == '"';
3430
3431 if text.eq_ignore_ascii_case("R") && (is_single_quote || is_double_quote_for_raw) {
3434 let quote_char = if is_single_quote { '\'' } else { '"' };
3437 self.advance(); if self.peek() == quote_char && self.peek_next() == quote_char {
3441 self.advance(); self.advance(); let string_value = self.scan_raw_triple_quoted_content(quote_char)?;
3445 self.add_token_with_text(TokenType::RawString, string_value);
3446 } else {
3447 let string_value = self.scan_raw_string_content(quote_char)?;
3448 self.add_token_with_text(TokenType::RawString, string_value);
3449 }
3450 return Ok(());
3451 }
3452
3453 if is_single_quote || is_double_quote {
3454 if text.eq_ignore_ascii_case("N") {
3455 self.advance(); let string_value = if is_single_quote {
3458 self.scan_string_content()?
3459 } else {
3460 self.scan_double_quoted_string_content()?
3461 };
3462 self.add_token_with_text(TokenType::NationalString, string_value);
3463 return Ok(());
3464 } else if text.eq_ignore_ascii_case("E") {
3465 let lowercase = text == "e";
3469 let prefix = if lowercase { "e:" } else { "E:" };
3470 self.advance(); let string_value = self.scan_string_content_with_escapes(true)?;
3472 self.add_token_with_text(
3473 TokenType::EscapeString,
3474 format!("{}{}", prefix, string_value),
3475 );
3476 return Ok(());
3477 } else if text.eq_ignore_ascii_case("X") {
3478 self.advance(); let string_value = if is_single_quote {
3481 self.scan_string_content()?
3482 } else {
3483 self.scan_double_quoted_string_content()?
3484 };
3485 self.add_token_with_text(TokenType::HexString, string_value);
3486 return Ok(());
3487 } else if text.eq_ignore_ascii_case("B") && is_double_quote {
3488 self.advance(); let string_value = self.scan_double_quoted_string_content()?;
3491 self.add_token_with_text(TokenType::ByteString, string_value);
3492 return Ok(());
3493 } else if text.eq_ignore_ascii_case("B") && is_single_quote {
3494 self.advance(); let string_value = self.scan_string_content()?;
3498 if self.config.b_prefix_is_byte_string {
3499 self.add_token_with_text(TokenType::ByteString, string_value);
3500 } else {
3501 self.add_token_with_text(TokenType::BitString, string_value);
3502 }
3503 return Ok(());
3504 }
3505 }
3506
3507 if text.eq_ignore_ascii_case("U")
3509 && self.peek() == '&'
3510 && self.current + 1 < self.size
3511 && self.char_at(self.current + 1) == '\''
3512 {
3513 self.advance(); self.advance(); let string_value = self.scan_string_content()?;
3516 self.add_token_with_text(TokenType::UnicodeString, string_value);
3517 return Ok(());
3518 }
3519
3520 let token_type = Self::lookup_keyword_ascii(&self.config.keywords, &text);
3521
3522 if let Some(text) = owned_text {
3523 self.add_token_with_text(token_type, text);
3524 } else {
3525 self.add_token_from_source(token_type, self.start, self.current);
3526 }
3527 Ok(())
3528 }
3529
3530 fn scan_string_content_with_escapes(
3534 &mut self,
3535 force_backslash_escapes: bool,
3536 ) -> Result<String> {
3537 let use_backslash_escapes =
3538 force_backslash_escapes || self.config.string_escapes.contains(&'\\');
3539 if let Some((start, end)) = self.try_scan_simple_quoted_content('\'', use_backslash_escapes)
3540 {
3541 return Ok(self.text_from_range(start, end));
3542 }
3543 let mut value = String::new();
3544
3545 while !self.is_at_end() {
3546 let c = self.peek();
3547 if c == '\'' {
3548 if self.peek_next() == '\'' {
3549 value.push('\'');
3551 self.advance();
3552 self.advance();
3553 } else {
3554 break;
3555 }
3556 } else if c == '\\' && use_backslash_escapes {
3557 value.push(self.advance());
3559 if !self.is_at_end() {
3560 value.push(self.advance());
3561 }
3562 } else {
3563 value.push(self.advance());
3564 }
3565 }
3566
3567 if self.is_at_end() {
3568 return Err(Error::tokenize(
3569 "Unterminated string",
3570 self.line,
3571 self.column,
3572 self.start,
3573 self.current,
3574 ));
3575 }
3576
3577 self.advance(); Ok(value)
3579 }
3580
3581 fn scan_string_content(&mut self) -> Result<String> {
3583 self.scan_string_content_with_escapes(false)
3584 }
3585
3586 fn scan_double_quoted_string_content(&mut self) -> Result<String> {
3589 let use_backslash_escapes = self.config.string_escapes.contains(&'\\');
3590 if let Some((start, end)) = self.try_scan_simple_quoted_content('"', use_backslash_escapes)
3591 {
3592 return Ok(self.text_from_range(start, end));
3593 }
3594 let mut value = String::new();
3595
3596 while !self.is_at_end() {
3597 let c = self.peek();
3598 if c == '"' {
3599 if self.peek_next() == '"' {
3600 value.push('"');
3602 self.advance();
3603 self.advance();
3604 } else {
3605 break;
3606 }
3607 } else if c == '\\' && use_backslash_escapes {
3608 self.advance(); if !self.is_at_end() {
3611 let escaped = self.advance();
3612 match escaped {
3613 'n' => value.push('\n'),
3614 'r' => value.push('\r'),
3615 't' => value.push('\t'),
3616 '0' => value.push('\0'),
3617 '\\' => value.push('\\'),
3618 '"' => value.push('"'),
3619 '\'' => value.push('\''),
3620 'x' => {
3621 let mut hex = String::new();
3623 for _ in 0..2 {
3624 if !self.is_at_end() && self.peek().is_ascii_hexdigit() {
3625 hex.push(self.advance());
3626 }
3627 }
3628 if let Ok(byte) = u8::from_str_radix(&hex, 16) {
3629 value.push(byte as char);
3630 } else {
3631 value.push('\\');
3633 value.push('x');
3634 value.push_str(&hex);
3635 }
3636 }
3637 _ => {
3638 value.push('\\');
3640 value.push(escaped);
3641 }
3642 }
3643 }
3644 } else {
3645 value.push(self.advance());
3646 }
3647 }
3648
3649 if self.is_at_end() {
3650 return Err(Error::tokenize(
3651 "Unterminated double-quoted string",
3652 self.line,
3653 self.column,
3654 self.start,
3655 self.current,
3656 ));
3657 }
3658
3659 self.advance(); Ok(value)
3661 }
3662
3663 fn scan_raw_string_content(&mut self, quote_char: char) -> Result<String> {
3668 if let Some((start, end)) = self.try_scan_simple_quoted_content(
3669 quote_char,
3670 self.config.string_escapes_allowed_in_raw_strings,
3671 ) {
3672 return Ok(self.text_from_range(start, end));
3673 }
3674 let mut value = String::new();
3675
3676 while !self.is_at_end() {
3677 let c = self.peek();
3678 if c == quote_char {
3679 if self.peek_next() == quote_char {
3680 value.push(quote_char);
3682 self.advance();
3683 self.advance();
3684 } else {
3685 break;
3686 }
3687 } else if c == '\\'
3688 && self.peek_next() == quote_char
3689 && self.config.string_escapes_allowed_in_raw_strings
3690 {
3691 value.push('\\');
3694 value.push(quote_char);
3695 self.advance(); self.advance(); } else {
3698 value.push(self.advance());
3700 }
3701 }
3702
3703 if self.is_at_end() {
3704 return Err(Error::tokenize(
3705 "Unterminated raw string",
3706 self.line,
3707 self.column,
3708 self.start,
3709 self.current,
3710 ));
3711 }
3712
3713 self.advance(); Ok(value)
3715 }
3716
3717 fn scan_raw_triple_quoted_content(&mut self, quote_char: char) -> Result<String> {
3720 let mut value = String::new();
3721
3722 while !self.is_at_end() {
3723 if self.peek() == quote_char {
3724 let mut quote_count = 0;
3725 while self.current + quote_count < self.size
3726 && self.char_at(self.current + quote_count) == quote_char
3727 {
3728 quote_count += 1;
3729 }
3730 if quote_count >= 3 {
3731 for _ in 0..quote_count - 3 {
3734 value.push(quote_char);
3735 }
3736 for _ in 0..quote_count {
3737 self.advance();
3738 }
3739 return Ok(value);
3740 }
3741 }
3742 let ch = self.advance();
3744 value.push(ch);
3745 }
3746
3747 Err(Error::tokenize(
3748 "Unterminated raw triple-quoted string",
3749 self.line,
3750 self.column,
3751 self.start,
3752 self.current,
3753 ))
3754 }
3755
3756 fn scan_dollar_identifier(&mut self) -> Result<()> {
3761 self.advance();
3763
3764 while !self.is_at_end() {
3766 let c = self.peek();
3767 if c.is_alphanumeric() || c == '_' || c == '$' {
3768 self.advance();
3769 } else {
3770 break;
3771 }
3772 }
3773
3774 self.add_token(TokenType::Var);
3775 Ok(())
3776 }
3777
3778 fn scan_tsql_identifier(&mut self) -> Result<()> {
3779 let first = self.advance();
3781
3782 if first == '#' && self.peek() == '#' {
3784 self.advance();
3785 }
3786
3787 if !self.advance_ascii_identifier() {
3789 while !self.is_at_end() {
3790 let c = self.peek();
3791 if c.is_alphanumeric() || c == '_' || c == '$' || c == '#' || c == '@' {
3792 self.advance();
3793 } else {
3794 break;
3795 }
3796 }
3797 }
3798
3799 self.add_token(TokenType::Var);
3801 Ok(())
3802 }
3803
3804 fn try_scan_insert_format_raw_data(&mut self) -> Option<String> {
3808 let len = self.tokens.len();
3809 if len < 3 {
3810 return None;
3811 }
3812
3813 let last = &self.tokens[len - 1];
3815 if last.text(self.source).eq_ignore_ascii_case("VALUES") {
3816 return None;
3817 }
3818 if !matches!(last.token_type(), TokenType::Var | TokenType::Identifier) {
3819 return None;
3820 }
3821
3822 let format_tok = &self.tokens[len - 2];
3824 if !format_tok.text(self.source).eq_ignore_ascii_case("FORMAT") {
3825 return None;
3826 }
3827
3828 let has_insert = self.tokens[..len - 2]
3830 .iter()
3831 .rev()
3832 .take(20)
3833 .any(|t| t.token_type() == TokenType::Insert);
3834 if !has_insert {
3835 return None;
3836 }
3837
3838 let raw_start = self.current;
3842 while !self.is_at_end() {
3843 let c = self.peek();
3844 if c == '\n' {
3845 let saved = self.current;
3847 self.advance(); while !self.is_at_end() && self.peek() == '\r' {
3850 self.advance();
3851 }
3852 if self.is_at_end() || self.peek() == '\n' {
3853 let raw = self.text_from_range(raw_start, saved);
3856 return Some(raw.trim().to_string());
3857 }
3858 } else {
3860 self.advance();
3861 }
3862 }
3863
3864 let raw = self.text_from_range(raw_start, self.current);
3866 let trimmed = raw.trim().to_string();
3867 if trimmed.is_empty() {
3868 None
3869 } else {
3870 Some(trimmed)
3871 }
3872 }
3873
3874 fn add_token(&mut self, token_type: TokenType) {
3875 self.add_token_from_source(token_type, self.start, self.current);
3876 }
3877
3878 fn add_token_from_source(&mut self, token_type: TokenType, text_start: usize, text_end: usize) {
3879 let span = Span::new(self.start, self.current, self.line, self.column);
3880 if let Some(stats) = &mut self.guard_stats {
3881 stats.observe(token_type, span);
3882 }
3883 let mut token = if self
3884 .cursor
3885 .source_range(self.source, text_start, text_end)
3886 .is_some()
3887 {
3888 T::from_source(
3889 token_type,
3890 self.source,
3891 text_start,
3892 text_end,
3893 span,
3894 self.shared_source.as_ref(),
3895 )
3896 } else {
3897 T::from_owned(
3898 token_type,
3899 self.cursor
3900 .text_from_range(self.source, text_start, text_end),
3901 span,
3902 )
3903 };
3904 token.comments_mut().append(&mut self.comments);
3905 self.tokens.push(token);
3906 }
3907
3908 fn add_token_with_text(&mut self, token_type: TokenType, text: String) {
3909 let span = Span::new(self.start, self.current, self.line, self.column);
3910 if let Some(stats) = &mut self.guard_stats {
3911 stats.observe(token_type, span);
3912 }
3913 let mut token = T::from_owned(token_type, text, span);
3914 token.comments_mut().append(&mut self.comments);
3915 self.tokens.push(token);
3916 }
3917}
3918
3919#[cfg(test)]
3920mod tests {
3921 use super::*;
3922
3923 #[test]
3924 fn ascii_fast_path_matches_character_buffer_path() {
3925 let tokenizer = Tokenizer::default();
3926 let inputs = [
3927 "SELECT a, b FROM t WHERE id IN (1, 2, 3)",
3928 "SELECT 'it''s', \"quoted\", $1 /* comment */ FROM schema.table",
3929 "INSERT INTO t VALUES (1, 'a'), (2, 'b'); UPDATE t SET value = 'c'",
3930 "SELECT $$body$$, $tag$content$tag$, 0xFF, 1.25e-2",
3931 ];
3932
3933 for sql in inputs {
3934 assert_eq!(
3935 tokenizer.tokenize(sql).unwrap(),
3936 tokenizer.tokenize_without_ascii_fast_path(sql).unwrap(),
3937 "tokenization differs for {sql}"
3938 );
3939 }
3940 }
3941
3942 #[test]
3943 fn parser_tokens_match_public_tokens() {
3944 let tokenizer = Tokenizer::default();
3945 let inputs = [
3946 "SELECT alpha, 123, 'plain' FROM schema.table WHERE id = 42",
3947 "SELECT 'it''s', $$body$$, $tag$content$tag$ /* comment */",
3948 "SELECT cafe, 'naive' FROM t\nWHERE value >= 1.25e-2",
3949 "SELECT cafe, 'caf\u{e9}', \u{3b4}elta FROM donn\u{e9}es",
3950 ];
3951
3952 for sql in inputs {
3953 let public = tokenizer.tokenize(sql).unwrap();
3954 let source: Arc<str> = Arc::from(sql);
3955 let (parser, stats) = tokenizer.tokenize_for_parser(&source).unwrap();
3956 let materialized = parser
3957 .iter()
3958 .map(|token| Token {
3959 token_type: token.token_type,
3960 text: token.text_owned(),
3961 span: token.span,
3962 comments: token.comments.clone(),
3963 trailing_comments: token.trailing_comments.clone(),
3964 })
3965 .collect::<Vec<_>>();
3966
3967 assert_eq!(
3968 materialized, public,
3969 "parser tokenization differs for {sql}"
3970 );
3971 assert_eq!(stats.token_count, public.len());
3972 }
3973 }
3974
3975 #[test]
3976 fn parser_tokens_borrow_unchanged_ascii_text() {
3977 let tokenizer = Tokenizer::default();
3978 let source: Arc<str> = Arc::from("SELECT alpha, 123, 'plain'");
3979 let (tokens, _) = tokenizer.tokenize_for_parser(&source).unwrap();
3980
3981 assert!(tokens
3982 .iter()
3983 .all(|token| matches!(&token.text, ParserTokenText::Source { .. })));
3984 }
3985
3986 #[test]
3987 fn test_simple_select() {
3988 let tokenizer = Tokenizer::default();
3989 let tokens = tokenizer.tokenize("SELECT 1").unwrap();
3990
3991 assert_eq!(tokens.len(), 2);
3992 assert_eq!(tokens[0].token_type, TokenType::Select);
3993 assert_eq!(tokens[1].token_type, TokenType::Number);
3994 assert_eq!(tokens[1].text, "1");
3995 }
3996
3997 #[test]
3998 fn test_select_with_identifier() {
3999 let tokenizer = Tokenizer::default();
4000 let tokens = tokenizer.tokenize("SELECT a, b FROM t").unwrap();
4001
4002 assert_eq!(tokens.len(), 6);
4003 assert_eq!(tokens[0].token_type, TokenType::Select);
4004 assert_eq!(tokens[1].token_type, TokenType::Var);
4005 assert_eq!(tokens[1].text, "a");
4006 assert_eq!(tokens[2].token_type, TokenType::Comma);
4007 assert_eq!(tokens[3].token_type, TokenType::Var);
4008 assert_eq!(tokens[3].text, "b");
4009 assert_eq!(tokens[4].token_type, TokenType::From);
4010 assert_eq!(tokens[5].token_type, TokenType::Var);
4011 assert_eq!(tokens[5].text, "t");
4012 }
4013
4014 #[test]
4015 fn test_string_literal() {
4016 let tokenizer = Tokenizer::default();
4017 let tokens = tokenizer.tokenize("SELECT 'hello'").unwrap();
4018
4019 assert_eq!(tokens.len(), 2);
4020 assert_eq!(tokens[1].token_type, TokenType::String);
4021 assert_eq!(tokens[1].text, "hello");
4022 }
4023
4024 #[test]
4025 fn test_escaped_string() {
4026 let tokenizer = Tokenizer::default();
4027 let tokens = tokenizer.tokenize("SELECT 'it''s'").unwrap();
4028
4029 assert_eq!(tokens.len(), 2);
4030 assert_eq!(tokens[1].token_type, TokenType::String);
4031 assert_eq!(tokens[1].text, "it's");
4032 }
4033
4034 #[test]
4035 fn test_escape_follow_chars_gate_builtin_decoding() {
4036 let mut config = TokenizerConfig::default();
4037 config.string_escapes.push('\\');
4038 config.escape_follow_chars = vec!['n'];
4039 let tokenizer = Tokenizer::new(config);
4040 let tokens = tokenizer.tokenize(r"SELECT '\n\a\f\Z\x21'").unwrap();
4041
4042 assert_eq!(tokens[1].text, "\nafZx21");
4043 }
4044
4045 #[test]
4046 fn test_configured_numeric_escapes_require_complete_sequences() {
4047 let mut config = TokenizerConfig::default();
4048 config.string_escapes.push('\\');
4049 config.escape_follow_chars = vec!['0', '1', '2', '3', '4', '5', '6', '7', 'x', 'u'];
4050 let tokenizer = Tokenizer::new(config);
4051 let tokens = tokenizer
4052 .tokenize(r"SELECT '\041\x21\u26c4-\777\x2\u26c'")
4053 .unwrap();
4054
4055 assert_eq!(tokens[1].text, "!!\u{26c4}-777x2u26c");
4056 }
4057
4058 #[test]
4059 fn test_terminal_backslash_quote_recovery() {
4060 let mut config = TokenizerConfig::default();
4061 config.string_escapes.push('\\');
4062 config.recover_terminal_backslash_quote = true;
4063 let tokenizer = Tokenizer::new(config);
4064 let tokens = tokenizer
4065 .tokenize("SHOW FUNCTIONS LIKE 'a\\' OR 1=1")
4066 .unwrap();
4067
4068 assert_eq!(tokens.len(), 8);
4069 assert_eq!(tokens[3].token_type, TokenType::String);
4070 assert_eq!(tokens[3].text, "a\\");
4071 assert_eq!(tokens[4].token_type, TokenType::Or);
4072 }
4073
4074 #[test]
4075 fn test_comments() {
4076 let tokenizer = Tokenizer::default();
4077 let tokens = tokenizer.tokenize("SELECT -- comment\n1").unwrap();
4078
4079 assert_eq!(tokens.len(), 2);
4080 assert_eq!(tokens[0].trailing_comments.len(), 1);
4083 assert_eq!(tokens[0].trailing_comments[0], " comment");
4084 }
4085
4086 #[test]
4087 fn test_comment_in_and_chain() {
4088 use crate::generator::Generator;
4089 use crate::parser::Parser;
4090
4091 let sql = "SELECT a FROM b WHERE foo\n-- c1\nAND bar\n-- c2\nAND bla";
4093 let ast = Parser::parse_sql(sql).unwrap();
4094 let mut gen = Generator::default();
4095 let output = gen.generate(&ast[0]).unwrap();
4096 assert_eq!(
4097 output,
4098 "SELECT a FROM b WHERE foo AND /* c1 */ bar AND /* c2 */ bla"
4099 );
4100 }
4101
4102 #[test]
4103 fn test_operators() {
4104 let tokenizer = Tokenizer::default();
4105 let tokens = tokenizer.tokenize("1 + 2 * 3").unwrap();
4106
4107 assert_eq!(tokens.len(), 5);
4108 assert_eq!(tokens[0].token_type, TokenType::Number);
4109 assert_eq!(tokens[1].token_type, TokenType::Plus);
4110 assert_eq!(tokens[2].token_type, TokenType::Number);
4111 assert_eq!(tokens[3].token_type, TokenType::Star);
4112 assert_eq!(tokens[4].token_type, TokenType::Number);
4113 }
4114
4115 #[test]
4116 fn test_comparison_operators() {
4117 let tokenizer = Tokenizer::default();
4118 let tokens = tokenizer.tokenize("a <= b >= c != d").unwrap();
4119
4120 assert_eq!(tokens[1].token_type, TokenType::Lte);
4121 assert_eq!(tokens[3].token_type, TokenType::Gte);
4122 assert_eq!(tokens[5].token_type, TokenType::Neq);
4123 }
4124
4125 #[test]
4126 fn test_national_string() {
4127 let tokenizer = Tokenizer::default();
4128 let tokens = tokenizer.tokenize("N'abc'").unwrap();
4129
4130 assert_eq!(
4131 tokens.len(),
4132 1,
4133 "Expected 1 token for N'abc', got {:?}",
4134 tokens
4135 );
4136 assert_eq!(tokens[0].token_type, TokenType::NationalString);
4137 assert_eq!(tokens[0].text, "abc");
4138 }
4139
4140 #[test]
4141 fn test_hex_string() {
4142 let tokenizer = Tokenizer::default();
4143 let tokens = tokenizer.tokenize("X'ABCD'").unwrap();
4144
4145 assert_eq!(
4146 tokens.len(),
4147 1,
4148 "Expected 1 token for X'ABCD', got {:?}",
4149 tokens
4150 );
4151 assert_eq!(tokens[0].token_type, TokenType::HexString);
4152 assert_eq!(tokens[0].text, "ABCD");
4153 }
4154
4155 #[test]
4156 fn test_bit_string() {
4157 let tokenizer = Tokenizer::default();
4158 let tokens = tokenizer.tokenize("B'01010'").unwrap();
4159
4160 assert_eq!(
4161 tokens.len(),
4162 1,
4163 "Expected 1 token for B'01010', got {:?}",
4164 tokens
4165 );
4166 assert_eq!(tokens[0].token_type, TokenType::BitString);
4167 assert_eq!(tokens[0].text, "01010");
4168 }
4169
4170 #[test]
4171 fn test_trailing_dot_number() {
4172 let tokenizer = Tokenizer::default();
4173
4174 let tokens = tokenizer.tokenize("SELECT 1.").unwrap();
4176 assert_eq!(
4177 tokens.len(),
4178 2,
4179 "Expected 2 tokens for 'SELECT 1.', got {:?}",
4180 tokens
4181 );
4182 assert_eq!(tokens[1].token_type, TokenType::Number);
4183 assert_eq!(tokens[1].text, "1.");
4184
4185 let tokens = tokenizer.tokenize("SELECT 1.5").unwrap();
4187 assert_eq!(tokens[1].text, "1.5");
4188
4189 let tokens = tokenizer.tokenize("SELECT 1.a").unwrap();
4192 assert_eq!(
4193 tokens.len(),
4194 3,
4195 "Expected 3 tokens for 'SELECT 1.a', got {:?}",
4196 tokens
4197 );
4198 assert_eq!(tokens[1].token_type, TokenType::Number);
4199 assert_eq!(tokens[1].text, "1.");
4200 assert_eq!(tokens[2].token_type, TokenType::Var);
4201
4202 let tokens = tokenizer.tokenize("SELECT 1..2").unwrap();
4204 assert_eq!(tokens[1].token_type, TokenType::Number);
4205 assert_eq!(tokens[1].text, "1");
4206 assert_eq!(tokens[2].token_type, TokenType::Dot);
4207 assert_eq!(tokens[3].token_type, TokenType::Dot);
4208 assert_eq!(tokens[4].token_type, TokenType::Number);
4209 assert_eq!(tokens[4].text, "2");
4210 }
4211
4212 #[test]
4213 fn test_leading_dot_number() {
4214 let tokenizer = Tokenizer::default();
4215
4216 let tokens = tokenizer.tokenize(".25").unwrap();
4218 assert_eq!(
4219 tokens.len(),
4220 1,
4221 "Expected 1 token for '.25', got {:?}",
4222 tokens
4223 );
4224 assert_eq!(tokens[0].token_type, TokenType::Number);
4225 assert_eq!(tokens[0].text, ".25");
4226
4227 let tokens = tokenizer.tokenize("SAMPLE (.25)").unwrap();
4229 assert_eq!(
4230 tokens.len(),
4231 4,
4232 "Expected 4 tokens for 'SAMPLE (.25)', got {:?}",
4233 tokens
4234 );
4235 assert_eq!(tokens[0].token_type, TokenType::Sample);
4236 assert_eq!(tokens[1].token_type, TokenType::LParen);
4237 assert_eq!(tokens[2].token_type, TokenType::Number);
4238 assert_eq!(tokens[2].text, ".25");
4239 assert_eq!(tokens[3].token_type, TokenType::RParen);
4240
4241 let tokens = tokenizer.tokenize(".5e10").unwrap();
4243 assert_eq!(
4244 tokens.len(),
4245 1,
4246 "Expected 1 token for '.5e10', got {:?}",
4247 tokens
4248 );
4249 assert_eq!(tokens[0].token_type, TokenType::Number);
4250 assert_eq!(tokens[0].text, ".5e10");
4251
4252 let tokens = tokenizer.tokenize("a.b").unwrap();
4254 assert_eq!(
4255 tokens.len(),
4256 3,
4257 "Expected 3 tokens for 'a.b', got {:?}",
4258 tokens
4259 );
4260 assert_eq!(tokens[1].token_type, TokenType::Dot);
4261 }
4262
4263 #[test]
4264 fn test_unrecognized_character() {
4265 let tokenizer = Tokenizer::default();
4266
4267 let result = tokenizer.tokenize("SELECT \u{2018}hello\u{2019}");
4269 assert!(
4270 result.is_ok(),
4271 "Curly quotes should be tokenized as strings"
4272 );
4273
4274 let result = tokenizer.tokenize("SELECT • FROM t");
4276 assert!(result.is_err());
4277 }
4278
4279 #[test]
4280 fn test_colon_eq_tokenization() {
4281 let tokenizer = Tokenizer::default();
4282
4283 let tokens = tokenizer.tokenize("a := 1").unwrap();
4285 assert_eq!(tokens.len(), 3);
4286 assert_eq!(tokens[0].token_type, TokenType::Var);
4287 assert_eq!(tokens[1].token_type, TokenType::ColonEq);
4288 assert_eq!(tokens[2].token_type, TokenType::Number);
4289
4290 let tokens = tokenizer.tokenize("a:b").unwrap();
4292 assert!(tokens.iter().any(|t| t.token_type == TokenType::Colon));
4293 assert!(!tokens.iter().any(|t| t.token_type == TokenType::ColonEq));
4294
4295 let tokens = tokenizer.tokenize("a::INT").unwrap();
4297 assert!(tokens.iter().any(|t| t.token_type == TokenType::DColon));
4298 }
4299
4300 #[test]
4301 fn test_colon_eq_parsing() {
4302 use crate::generator::Generator;
4303 use crate::parser::Parser;
4304
4305 let ast = Parser::parse_sql("SELECT @var1 := 1, @var2")
4307 .expect("Failed to parse MySQL @var := expr");
4308 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4309 assert_eq!(output, "SELECT @var1 := 1, @var2");
4310
4311 let ast = Parser::parse_sql("SELECT @var1, @var2 := @var1")
4313 .expect("Failed to parse MySQL @var2 := @var1");
4314 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4315 assert_eq!(output, "SELECT @var1, @var2 := @var1");
4316
4317 let ast = Parser::parse_sql("SELECT @var1 := COUNT(*) FROM t1")
4319 .expect("Failed to parse MySQL @var := COUNT(*)");
4320 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4321 assert_eq!(output, "SELECT @var1 := COUNT(*) FROM t1");
4322
4323 let ast = Parser::parse_sql("SET @var1 := 1").expect("Failed to parse SET @var1 := 1");
4325 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4326 assert_eq!(output, "SET @var1 = 1");
4327
4328 let ast =
4330 Parser::parse_sql("UNION_VALUE(k1 := 1)").expect("Failed to parse named arg with :=");
4331 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4332 assert_eq!(output, "UNION_VALUE(k1 := 1)");
4333
4334 let ast = Parser::parse_sql("SELECT UNNEST(col, recursive := TRUE) FROM t")
4336 .expect("Failed to parse UNNEST with :=");
4337 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4338 assert_eq!(output, "SELECT UNNEST(col, recursive := TRUE) FROM t");
4339
4340 let ast =
4342 Parser::parse_sql("SELECT foo: 1").expect("Failed to parse DuckDB prefix alias foo: 1");
4343 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4344 assert_eq!(output, "SELECT 1 AS foo");
4345
4346 let ast = Parser::parse_sql("SELECT foo: 1, bar: 2, baz: 3")
4348 .expect("Failed to parse DuckDB multiple prefix aliases");
4349 let output = Generator::sql(&ast[0]).expect("Failed to generate");
4350 assert_eq!(output, "SELECT 1 AS foo, 2 AS bar, 3 AS baz");
4351 }
4352
4353 #[test]
4354 fn test_colon_eq_dialect_roundtrip() {
4355 use crate::dialects::{Dialect, DialectType};
4356
4357 fn check(dialect: DialectType, sql: &str, expected: Option<&str>) {
4358 let d = Dialect::get(dialect);
4359 let ast = d
4360 .parse(sql)
4361 .unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
4362 assert!(!ast.is_empty(), "Empty AST for: {}", sql);
4363 let transformed = d
4364 .transform(ast[0].clone())
4365 .unwrap_or_else(|e| panic!("Transform error for '{}': {}", sql, e));
4366 let output = d
4367 .generate(&transformed)
4368 .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e));
4369 let expected = expected.unwrap_or(sql);
4370 assert_eq!(output, expected, "Roundtrip failed for: {}", sql);
4371 }
4372
4373 check(DialectType::MySQL, "SELECT @var1 := 1, @var2", None);
4375 check(DialectType::MySQL, "SELECT @var1, @var2 := @var1", None);
4376 check(DialectType::MySQL, "SELECT @var1 := COUNT(*) FROM t1", None);
4377 check(DialectType::MySQL, "SET @var1 := 1", Some("SET @var1 = 1"));
4378
4379 check(
4381 DialectType::DuckDB,
4382 "SELECT UNNEST(col, recursive := TRUE) FROM t",
4383 None,
4384 );
4385 check(DialectType::DuckDB, "UNION_VALUE(k1 := 1)", None);
4386
4387 {
4390 let d = Dialect::get(DialectType::DuckDB);
4391 let ast = d
4392 .parse("STRUCT_PACK(a := 'b')::json")
4393 .expect("Failed to parse STRUCT_PACK(a := 'b')::json");
4394 assert!(!ast.is_empty(), "Empty AST for STRUCT_PACK(a := 'b')::json");
4395 }
4396
4397 check(
4399 DialectType::DuckDB,
4400 "SELECT foo: 1",
4401 Some("SELECT 1 AS foo"),
4402 );
4403 check(
4404 DialectType::DuckDB,
4405 "SELECT foo: 1, bar: 2, baz: 3",
4406 Some("SELECT 1 AS foo, 2 AS bar, 3 AS baz"),
4407 );
4408 }
4409
4410 #[test]
4411 fn test_comment_roundtrip() {
4412 use crate::generator::Generator;
4413 use crate::parser::Parser;
4414
4415 fn check_roundtrip(sql: &str) -> Option<String> {
4416 let ast = match Parser::parse_sql(sql) {
4417 Ok(a) => a,
4418 Err(e) => return Some(format!("Parse error: {:?}", e)),
4419 };
4420 if ast.is_empty() {
4421 return Some("Empty AST".to_string());
4422 }
4423 let mut generator = Generator::default();
4424 let output = match generator.generate(&ast[0]) {
4425 Ok(o) => o,
4426 Err(e) => return Some(format!("Gen error: {:?}", e)),
4427 };
4428 if output == sql {
4429 None
4430 } else {
4431 Some(format!(
4432 "Mismatch:\n input: {}\n output: {}",
4433 sql, output
4434 ))
4435 }
4436 }
4437
4438 let tests = vec![
4439 "SELECT c /* c1 */ AS alias /* c2 */",
4445 "SELECT a /* x */, b /* x */",
4447 "SELECT a /* x */ /* y */ /* z */, b /* k */ /* m */",
4449 "SELECT * FROM foo /* x */, bla /* x */",
4451 "SELECT 1 /* comment */ + 1",
4453 "SELECT 1 /* c1 */ + 2 /* c2 */",
4454 "SELECT 1 /* c1 */ + /* c2 */ 2 /* c3 */",
4455 "SELECT CAST(x AS INT) /* comment */ FROM foo",
4457 "SELECT FOO(x /* c */) /* FOO */, b /* b */",
4459 "SELECT x FROM a.b.c /* x */, e.f.g /* x */",
4461 "INSERT INTO t1 (tc1 /* tc1 */, tc2 /* tc2 */) SELECT c1 /* sc1 */, c2 /* sc2 */ FROM t",
4463 "/* c */ WITH x AS (SELECT 1) SELECT * FROM x",
4465 "/* comment1 */ INSERT INTO x /* comment2 */ VALUES (1, 2, 3)",
4466 "/* comment1 */ UPDATE tbl /* comment2 */ SET x = 2 WHERE x < 2",
4467 "/* comment1 */ DELETE FROM x /* comment2 */ WHERE y > 1",
4468 "/* comment */ CREATE TABLE foo AS SELECT 1",
4469 "INSERT INTO foo SELECT * FROM bar /* comment */",
4471 "SELECT FOO(x /* c1 */ + y /* c2 */ + BLA(5 /* c3 */)) FROM (VALUES (1 /* c4 */, \"test\" /* c5 */)) /* c6 */",
4473 ];
4474
4475 let mut failures = Vec::new();
4476 for sql in tests {
4477 if let Some(e) = check_roundtrip(sql) {
4478 failures.push(e);
4479 }
4480 }
4481
4482 if !failures.is_empty() {
4483 panic!("Comment roundtrip failures:\n{}", failures.join("\n\n"));
4484 }
4485 }
4486
4487 #[test]
4488 fn test_dollar_quoted_string_parsing() {
4489 use crate::dialects::{Dialect, DialectType};
4490
4491 let (tag, content) = super::parse_dollar_string_token("FOO\x00content here");
4493 assert_eq!(tag, Some("FOO".to_string()));
4494 assert_eq!(content, "content here");
4495
4496 let (tag, content) = super::parse_dollar_string_token("just content");
4497 assert_eq!(tag, None);
4498 assert_eq!(content, "just content");
4499
4500 fn check_databricks(sql: &str, expected: Option<&str>) {
4502 let d = Dialect::get(DialectType::Databricks);
4503 let ast = d
4504 .parse(sql)
4505 .unwrap_or_else(|e| panic!("Parse error for '{}': {}", sql, e));
4506 assert!(!ast.is_empty(), "Empty AST for: {}", sql);
4507 let transformed = d
4508 .transform(ast[0].clone())
4509 .unwrap_or_else(|e| panic!("Transform error for '{}': {}", sql, e));
4510 let output = d
4511 .generate(&transformed)
4512 .unwrap_or_else(|e| panic!("Generate error for '{}': {}", sql, e));
4513 let expected = expected.unwrap_or(sql);
4514 assert_eq!(output, expected, "Roundtrip failed for: {}", sql);
4515 }
4516
4517 check_databricks(
4519 "CREATE FUNCTION add_one(x INT) RETURNS INT LANGUAGE PYTHON AS $$def add_one(x):\n return x+1$$",
4520 None
4521 );
4522
4523 check_databricks(
4525 "CREATE FUNCTION add_one(x INT) RETURNS INT LANGUAGE PYTHON AS $FOO$def add_one(x):\n return x+1$FOO$",
4526 None
4527 );
4528 }
4529
4530 #[test]
4531 fn test_numeric_underscore_stripping() {
4532 let mut config = TokenizerConfig::default();
4534 config.numbers_can_be_underscore_separated = true;
4535 let tokenizer = Tokenizer::new(config);
4536
4537 let tokens = tokenizer.tokenize("SELECT 1_2_3_4_5").unwrap();
4539 assert_eq!(tokens[1].token_type, TokenType::Number);
4540 assert_eq!(tokens[1].text, "12345");
4541
4542 let tokens = tokenizer.tokenize("SELECT 20_000").unwrap();
4544 assert_eq!(tokens[1].token_type, TokenType::Number);
4545 assert_eq!(tokens[1].text, "20000");
4546
4547 let tokens = tokenizer.tokenize("SELECT 1_2E+1_0").unwrap();
4549 assert_eq!(tokens[1].token_type, TokenType::Number);
4550 assert_eq!(tokens[1].text, "12E+10");
4551
4552 let default_tokenizer = Tokenizer::default();
4554 let tokens = default_tokenizer.tokenize("SELECT 1_2_3_4_5").unwrap();
4555 assert_eq!(tokens[1].token_type, TokenType::Number);
4556 assert_eq!(tokens[1].text, "1_2_3_4_5");
4557 }
4558}