11mod cursor;
22mod token;
33use cursor:: { Cursor , EOF_CHAR } ;
4- pub use token:: { Base , LiteralKind , Token , TokenKind } ;
4+ pub use token:: { Base , LiteralKind , NamedParamKind , Token , TokenKind } ;
55
66// via: https://github.com/postgres/postgres/blob/db0c96cc18aec417101e37e59fcc53d4bf647915/src/backend/parser/scan.l#L346
77// ident_start [A-Za-z\200-\377_]
@@ -132,6 +132,46 @@ impl Cursor<'_> {
132132 }
133133 _ => TokenKind :: Dot ,
134134 } ,
135+ '@' => {
136+ if is_ident_start ( self . first ( ) ) {
137+ // Named parameter with @ prefix.
138+ self . eat_while ( is_ident_cont) ;
139+ TokenKind :: NamedParam {
140+ kind : NamedParamKind :: AtPrefix ,
141+ }
142+ } else {
143+ TokenKind :: At
144+ }
145+ }
146+ ':' => {
147+ // Named parameters in psql with different substitution styles.
148+ //
149+ // https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-INTERPOLATION
150+ match self . first ( ) {
151+ '\'' => {
152+ // Named parameter with colon prefix and single quotes.
153+ self . bump ( ) ;
154+ let terminated = self . single_quoted_string ( ) ;
155+ let kind = NamedParamKind :: ColonString { terminated } ;
156+ TokenKind :: NamedParam { kind }
157+ }
158+ '"' => {
159+ // Named parameter with colon prefix and double quotes.
160+ self . bump ( ) ;
161+ let terminated = self . double_quoted_string ( ) ;
162+ let kind = NamedParamKind :: ColonIdentifier { terminated } ;
163+ TokenKind :: NamedParam { kind }
164+ }
165+ c if is_ident_start ( c) => {
166+ // Named parameter with colon prefix.
167+ self . eat_while ( is_ident_cont) ;
168+ TokenKind :: NamedParam {
169+ kind : NamedParamKind :: ColonRaw ,
170+ }
171+ }
172+ _ => TokenKind :: Colon ,
173+ }
174+ }
135175 // One-symbol tokens.
136176 ';' => TokenKind :: Semi ,
137177 '\\' => TokenKind :: Backslash ,
@@ -140,11 +180,9 @@ impl Cursor<'_> {
140180 ')' => TokenKind :: CloseParen ,
141181 '[' => TokenKind :: OpenBracket ,
142182 ']' => TokenKind :: CloseBracket ,
143- '@' => TokenKind :: At ,
144183 '#' => TokenKind :: Pound ,
145184 '~' => TokenKind :: Tilde ,
146185 '?' => TokenKind :: Question ,
147- ':' => TokenKind :: Colon ,
148186 '$' => {
149187 // Dollar quoted strings
150188 if is_ident_start ( self . first ( ) ) || self . first ( ) == '$' {
@@ -613,6 +651,31 @@ mod tests {
613651 }
614652 tokens
615653 }
654+
655+ #[ test]
656+ fn named_param_at ( ) {
657+ let result = lex ( "select 1 from c where id = @id;" ) ;
658+ assert_debug_snapshot ! ( result) ;
659+ }
660+
661+ #[ test]
662+ fn named_param_colon_raw ( ) {
663+ let result = lex ( "select 1 from c where id = :id;" ) ;
664+ assert_debug_snapshot ! ( result) ;
665+ }
666+
667+ #[ test]
668+ fn named_param_colon_string ( ) {
669+ let result = lex ( "select 1 from c where id = :'id';" ) ;
670+ assert_debug_snapshot ! ( result) ;
671+ }
672+
673+ #[ test]
674+ fn named_param_colon_identifier ( ) {
675+ let result = lex ( "select 1 from c where id = :\" id\" ;" ) ;
676+ assert_debug_snapshot ! ( result) ;
677+ }
678+
616679 #[ test]
617680 fn lex_statement ( ) {
618681 let result = lex ( "select 1;" ) ;
0 commit comments