Skip to content

Commit 4ca1db4

Browse files
author
Markus Westerlind
committed
fix: Handle double quotes inside quoted identifiers correctly
This fixes apache#410 for standard SQL, however I don't know enough about other dialects to know if they handle this differently. May need more extensive testing as well.
1 parent ea0eb1b commit 4ca1db4

File tree

3 files changed

+36
-4
lines changed

3 files changed

+36
-4
lines changed

src/ast/mod.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use alloc::{
2323
string::{String, ToString},
2424
vec::Vec,
2525
};
26-
use core::fmt;
26+
use core::fmt::{self, Write};
2727

2828
#[cfg(feature = "serde")]
2929
use serde::{Deserialize, Serialize};
@@ -127,7 +127,16 @@ impl From<&str> for Ident {
127127
impl fmt::Display for Ident {
128128
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129129
match self.quote_style {
130-
Some(q) if q == '"' || q == '\'' || q == '`' => write!(f, "{}{}{}", q, self.value, q),
130+
Some(q) if q == '"' || q == '\'' || q == '`' => {
131+
f.write_char(q)?;
132+
for s in self.value.split_inclusive(q) {
133+
f.write_str(s)?;
134+
if s.ends_with(q) {
135+
f.write_char(q)?;
136+
}
137+
}
138+
f.write_char(q)
139+
}
131140
Some(q) if q == '[' => write!(f, "[{}]", self.value),
132141
None => f.write_str(&self.value),
133142
_ => panic!("unexpected quote style"),

src/tokenizer.rs

+20-2
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,26 @@ impl<'a> Tokenizer<'a> {
418418
quote_start if self.dialect.is_delimited_identifier_start(quote_start) => {
419419
chars.next(); // consume the opening quote
420420
let quote_end = Word::matching_end_quote(quote_start);
421-
let s = peeking_take_while(chars, |ch| ch != quote_end);
422-
if chars.next() == Some(quote_end) {
421+
let mut last_char = None;
422+
let s = {
423+
let mut s = String::new();
424+
while let Some(ch) = chars.next() {
425+
if ch == quote_end {
426+
if chars.peek() == Some(&quote_end) {
427+
chars.next();
428+
s.push(ch);
429+
} else {
430+
last_char = Some(quote_end);
431+
break;
432+
}
433+
} else {
434+
s.push(ch);
435+
}
436+
}
437+
s
438+
};
439+
440+
if last_char == Some(quote_end) {
423441
Ok(Some(Token::make_word(&s, Some(quote_start))))
424442
} else {
425443
self.tokenizer_error(format!(

tests/sqlparser_postgres.rs

+5
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,11 @@ fn parse_comments() {
834834
}
835835
}
836836

837+
#[test]
838+
fn parse_quoted_identifier() {
839+
pg_and_generic().verified_stmt(r#"SELECT "quoted "" ident""#);
840+
}
841+
837842
fn pg() -> TestedDialects {
838843
TestedDialects {
839844
dialects: vec![Box::new(PostgreSqlDialect {})],

0 commit comments

Comments
 (0)