@@ -38,6 +38,10 @@ pub enum Token {
38
38
NationalStringLiteral ( String ) ,
39
39
/// Hexadecimal string literal: i.e.: X'deadbeef'
40
40
HexStringLiteral ( String ) ,
41
+ /// An unsigned numeric literal representing positional
42
+ /// parameters like $1, $2, etc. in prepared statements and
43
+ /// function definitions
44
+ Parameter ( String ) ,
41
45
/// Comma
42
46
Comma ,
43
47
/// Whitespace (space, tab, etc)
@@ -99,6 +103,7 @@ impl fmt::Display for Token {
99
103
Token :: SingleQuotedString ( ref s) => write ! ( f, "'{}'" , s) ,
100
104
Token :: NationalStringLiteral ( ref s) => write ! ( f, "N'{}'" , s) ,
101
105
Token :: HexStringLiteral ( ref s) => write ! ( f, "X'{}'" , s) ,
106
+ Token :: Parameter ( ref s) => write ! ( f, "$'{}'" , s) ,
102
107
Token :: Comma => f. write_str ( "," ) ,
103
108
Token :: Whitespace ( ws) => write ! ( f, "{}" , ws) ,
104
109
Token :: Eq => f. write_str ( "=" ) ,
@@ -249,6 +254,7 @@ impl<'a> Tokenizer<'a> {
249
254
Token :: Word ( w) if w. quote_style != None => self . col += w. value . len ( ) as u64 + 2 ,
250
255
Token :: Number ( s) => self . col += s. len ( ) as u64 ,
251
256
Token :: SingleQuotedString ( s) => self . col += s. len ( ) as u64 ,
257
+ Token :: Parameter ( s) => self . col += s. len ( ) as u64 ,
252
258
_ => self . col += 1 ,
253
259
}
254
260
@@ -259,7 +265,7 @@ impl<'a> Tokenizer<'a> {
259
265
260
266
/// Get the next token or return None
261
267
fn next_token ( & self , chars : & mut Peekable < Chars < ' _ > > ) -> Result < Option < Token > , TokenizerError > {
262
- // println!("next_token: {:?}", chars.peek());
268
+ // println!("next_token: {:?}", chars.peek());
263
269
match chars. peek ( ) {
264
270
Some ( & ch) => match ch {
265
271
' ' => self . consume_and_return ( chars, Token :: Whitespace ( Whitespace :: Space ) ) ,
@@ -415,6 +421,7 @@ impl<'a> Tokenizer<'a> {
415
421
'&' => self . consume_and_return ( chars, Token :: Ampersand ) ,
416
422
'{' => self . consume_and_return ( chars, Token :: LBrace ) ,
417
423
'}' => self . consume_and_return ( chars, Token :: RBrace ) ,
424
+ '$' => self . tokenize_positional_parameter ( chars) ,
418
425
other => self . consume_and_return ( chars, Token :: Char ( other) ) ,
419
426
} ,
420
427
None => Ok ( None ) ,
@@ -490,6 +497,27 @@ impl<'a> Tokenizer<'a> {
490
497
}
491
498
}
492
499
500
+ /// PostgreSQL supports positional parameters (like $1, $2, etc.) for prepared statements
501
+ /// and function definitions.
502
+ /// Grab the positional argument following a $ to parse it.
503
+ fn tokenize_positional_parameter ( & self , chars : & mut Peekable < Chars < ' _ > > ) -> Result < Option < Token > , TokenizerError > {
504
+ assert_eq ! ( Some ( & '$' ) , chars. peek( ) ) ;
505
+ chars. next ( ) ; // throw away $
506
+
507
+ // Right now, following block copied from number parsing above...
508
+ // TODO: https://jakewheat.github.io/sql-overview/sql-2011-foundation-grammar.html#unsigned-numeric-literal
509
+ let position = peeking_take_while ( chars, |ch| match ch {
510
+ '0' ..='9' | '$' => true ,
511
+ _ => false ,
512
+ } ) ;
513
+ // End copied block
514
+
515
+ let new_token = Token :: Parameter ( position) ;
516
+ println ! ( "new token: {:#?}" , new_token) ;
517
+
518
+ Ok ( Some ( new_token) )
519
+ }
520
+
493
521
fn consume_and_return (
494
522
& self ,
495
523
chars : & mut Peekable < Chars < ' _ > > ,
0 commit comments