Skip to content

Commit 07d66a9

Browse files
authored
Merge pull request #53 from thomas-jeepe/master
Fix qualified wildcard stringifying
2 parents 4cdd003 + 76d2d46 commit 07d66a9

File tree

4 files changed

+47
-3
lines changed

4 files changed

+47
-3
lines changed

src/sqlast/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ impl ToString for ASTNode {
111111
match self {
112112
ASTNode::SQLIdentifier(s) => s.to_string(),
113113
ASTNode::SQLWildcard => "*".to_string(),
114-
ASTNode::SQLQualifiedWildcard(q) => q.join(".") + "*",
114+
ASTNode::SQLQualifiedWildcard(q) => q.join(".") + ".*",
115115
ASTNode::SQLCompoundIdentifier(s) => s.join("."),
116116
ASTNode::SQLIsNull(ast) => format!("{} IS NULL", ast.as_ref().to_string()),
117117
ASTNode::SQLIsNotNull(ast) => format!("{} IS NOT NULL", ast.as_ref().to_string()),

src/sqlast/value.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ impl ToString for Value {
3535
Value::Long(v) => v.to_string(),
3636
Value::Double(v) => v.to_string(),
3737
Value::Uuid(v) => v.to_string(),
38-
Value::SingleQuotedString(v) => format!("'{}'", v),
38+
Value::SingleQuotedString(v) => format!("'{}'", escape_single_quote_string(v)),
3939
Value::NationalStringLiteral(v) => format!("N'{}'", v),
4040
Value::Boolean(v) => v.to_string(),
4141
Value::Date(v) => v.to_string(),
@@ -46,3 +46,15 @@ impl ToString for Value {
4646
}
4747
}
4848
}
49+
50+
fn escape_single_quote_string(s: &str) -> String {
51+
let mut escaped = String::new();
52+
for c in s.chars() {
53+
if c == '\'' {
54+
escaped.push_str("\'\'");
55+
} else {
56+
escaped.push(c);
57+
}
58+
}
59+
escaped
60+
}

src/sqltokenizer.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,13 @@ impl<'a> Tokenizer<'a> {
462462
match ch {
463463
'\'' => {
464464
chars.next(); // consume
465-
break;
465+
let escaped_quote = chars.peek().map(|c| *c == '\'').unwrap_or(false);
466+
if escaped_quote {
467+
s.push('\'');
468+
chars.next();
469+
} else {
470+
break;
471+
}
466472
}
467473
_ => {
468474
chars.next(); // consume

tests/sqlparser_generic.rs

+26
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ fn parse_select_wildcard() {
9090
);
9191
}
9292

93+
#[test]
94+
fn parse_count_wildcard() {
95+
verified_only_select(
96+
"SELECT COUNT(Employee.*) FROM Order JOIN Employee ON Order.employee = Employee.id",
97+
);
98+
}
99+
93100
#[test]
94101
fn parse_column_aliases() {
95102
let sql = "SELECT a.col + 1 AS newname FROM foo AS a";
@@ -147,6 +154,25 @@ fn parse_projection_nested_type() {
147154
//TODO: add assertions
148155
}
149156

157+
#[test]
158+
fn parse_escaped_single_quote_string_predicate() {
159+
use self::ASTNode::*;
160+
use self::SQLOperator::*;
161+
let sql = "SELECT id, fname, lname FROM customer \
162+
WHERE salary != 'Jim''s salary'";
163+
let ast = verified_only_select(sql);
164+
assert_eq!(
165+
Some(SQLBinaryExpr {
166+
left: Box::new(SQLIdentifier("salary".to_string())),
167+
op: NotEq,
168+
right: Box::new(SQLValue(Value::SingleQuotedString(
169+
"Jim's salary".to_string()
170+
)))
171+
}),
172+
ast.selection,
173+
);
174+
}
175+
150176
#[test]
151177
fn parse_compound_expr_1() {
152178
use self::ASTNode::*;

0 commit comments

Comments
 (0)