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