@@ -1046,12 +1046,13 @@ impl<'a> Tokenizer<'a> {
10461046 Some ( & q @ 'q' ) | Some ( & q @ 'Q' ) if dialect_of ! ( self is OracleDialect | GenericDialect ) =>
10471047 {
10481048 chars. next ( ) ; // consume and check the next char
1049- self . tokenize_word_or_quote_delimited_string (
1050- chars,
1051- & [ n, q] ,
1052- Token :: NationalQuoteDelimitedStringLiteral ,
1053- )
1054- . map ( Some )
1049+ if let Some ( '\'' ) = chars. peek ( ) {
1050+ self . tokenize_quote_delimited_string ( chars, & [ n, q] )
1051+ . map ( |s| Some ( Token :: NationalQuoteDelimitedStringLiteral ( s) ) )
1052+ } else {
1053+ let s = self . tokenize_word ( String :: from_iter ( [ n, q] ) , chars) ;
1054+ Ok ( Some ( Token :: make_word ( & s, None ) ) )
1055+ }
10551056 }
10561057 _ => {
10571058 // regular identifier starting with an "N"
@@ -1062,12 +1063,13 @@ impl<'a> Tokenizer<'a> {
10621063 }
10631064 q @ 'Q' | q @ 'q' if dialect_of ! ( self is OracleDialect | GenericDialect ) => {
10641065 chars. next ( ) ; // consume and check the next char
1065- self . tokenize_word_or_quote_delimited_string (
1066- chars,
1067- & [ q] ,
1068- Token :: QuoteDelimitedStringLiteral ,
1069- )
1070- . map ( Some )
1066+ if let Some ( '\'' ) = chars. peek ( ) {
1067+ self . tokenize_quote_delimited_string ( chars, & [ q] )
1068+ . map ( |s| Some ( Token :: QuoteDelimitedStringLiteral ( s) ) )
1069+ } else {
1070+ let s = self . tokenize_word ( q, chars) ;
1071+ Ok ( Some ( Token :: make_word ( & s, None ) ) )
1072+ }
10711073 }
10721074 // PostgreSQL accepts "escape" string constants, which are an extension to the SQL standard.
10731075 x @ 'e' | x @ 'E' if self . dialect . supports_string_escape_constant ( ) => {
@@ -2024,72 +2026,61 @@ impl<'a> Tokenizer<'a> {
20242026 )
20252027 }
20262028
2027- /// Reads a quote delimited string without "backslash escaping" or a word
2028- /// depending on `chars.next()` delivering a `'`.
2029+ /// Reads a quote delimited string expecting `chars.next()` to deliver a quote.
20292030 ///
20302031 /// See <https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Literals.html#GUID-1824CBAA-6E16-4921-B2A6-112FB02248DA>
2031- fn tokenize_word_or_quote_delimited_string (
2032+ fn tokenize_quote_delimited_string (
20322033 & self ,
20332034 chars : & mut State ,
20342035 // the prefix that introduced the possible literal or word,
20352036 // e.g. "Q" or "nq"
2036- word_prefix : & [ char ] ,
2037- // turns an identified quote string literal,
2038- // ie. `(start-quote-char, string-literal, end-quote-char)`
2039- // into a token
2040- as_literal : fn ( QuoteDelimitedString ) -> Token ,
2041- ) -> Result < Token , TokenizerError > {
2042- match chars. peek ( ) {
2043- Some ( '\'' ) => {
2044- chars. next ( ) ;
2045- // ~ determine the "quote character(s)"
2046- let error_loc = chars. location ( ) ;
2047- let ( start_quote, end_quote) = match chars. next ( ) {
2048- // ~ "newline" is not allowed by Oracle's SQL Reference,
2049- // but works with sql*plus nevertheless
2050- None | Some ( ' ' ) | Some ( '\t' ) | Some ( '\r' ) | Some ( '\n' ) => {
2051- return self . tokenizer_error (
2052- error_loc,
2053- format ! (
2054- "Invalid space, tab, newline, or EOF after '{}''." ,
2055- String :: from_iter( word_prefix)
2056- ) ,
2057- ) ;
2058- }
2059- Some ( c) => (
2060- c,
2061- match c {
2062- '[' => ']' ,
2063- '{' => '}' ,
2064- '<' => '>' ,
2065- '(' => ')' ,
2066- c => c,
2067- } ,
2037+ literal_prefix : & [ char ] ,
2038+ ) -> Result < QuoteDelimitedString , TokenizerError > {
2039+ let literal_start_loc = chars. location ( ) ;
2040+ chars. next ( ) ;
2041+
2042+ let start_quote_loc = chars. location ( ) ;
2043+ let ( start_quote, end_quote) = match chars. next ( ) {
2044+ // ~ "newline" is not allowed by Oracle's SQL Reference,
2045+ // but works with sql*plus nevertheless
2046+ None | Some ( ' ' ) | Some ( '\t' ) | Some ( '\r' ) | Some ( '\n' ) => {
2047+ return self . tokenizer_error (
2048+ start_quote_loc,
2049+ format ! (
2050+ "Invalid space, tab, newline, or EOF after '{}''." ,
2051+ String :: from_iter( literal_prefix)
20682052 ) ,
2069- } ;
2070- // read the string literal until the "quote character" following a by literal quote
2071- let mut value = String :: new ( ) ;
2072- while let Some ( ch) = chars. next ( ) {
2073- if ch == end_quote {
2074- if let Some ( '\'' ) = chars. peek ( ) {
2075- chars. next ( ) ; // ~ consume the quote
2076- return Ok ( as_literal ( QuoteDelimitedString {
2077- start_quote,
2078- value,
2079- end_quote,
2080- } ) ) ;
2081- }
2082- }
2083- value. push ( ch) ;
2084- }
2085- self . tokenizer_error ( error_loc, "Unterminated string literal" )
2053+ ) ;
20862054 }
2087- // ~ not a literal introduced with _token_prefix_, assm
2088- _ => {
2089- let s = self . tokenize_word ( String :: from_iter ( word_prefix) , chars) ;
2090- Ok ( Token :: make_word ( & s, None ) )
2055+ Some ( c) => (
2056+ c,
2057+ match c {
2058+ '[' => ']' ,
2059+ '{' => '}' ,
2060+ '<' => '>' ,
2061+ '(' => ')' ,
2062+ c => c,
2063+ } ,
2064+ ) ,
2065+ } ;
2066+
2067+ // read the string literal until the "quote character" following a by literal quote
2068+ let mut value = String :: new ( ) ;
2069+ while let Some ( ch) = chars. next ( ) {
2070+ if ch == end_quote {
2071+ if let Some ( '\'' ) = chars. peek ( ) {
2072+ chars. next ( ) ; // ~ consume the quote
2073+ return Ok ( QuoteDelimitedString {
2074+ start_quote,
2075+ value,
2076+ end_quote,
2077+ } ) ;
2078+ }
20912079 }
2080+ value. push ( ch) ;
20922081 }
2082+
2083+ self . tokenizer_error ( literal_start_loc, "Unterminated string literal" )
20932084 }
20942085
20952086 /// Read a quoted string.
0 commit comments