Skip to content

Commit 85d5363

Browse files
authored
feat: support double quoted literal strings for dialects(such as mysql,bigquery,spark) (#3056)
* support double quoted string. * add test. * add case sensitie test case. * fix naming error.
1 parent c95bb74 commit 85d5363

2 files changed

Lines changed: 74 additions & 5 deletions

File tree

datafusion/core/tests/sql/select.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,3 +1210,39 @@ async fn unprojected_filter() {
12101210
];
12111211
assert_batches_sorted_eq!(expected, &results);
12121212
}
1213+
1214+
#[tokio::test]
1215+
async fn case_sensitive_in_default_dialect() {
1216+
let int32_array = Int32Array::from(vec![1, 2, 3, 4, 5]);
1217+
let schema = Schema::new(vec![Field::new("INT32", DataType::Int32, false)]);
1218+
let batch =
1219+
RecordBatch::try_new(Arc::new(schema), vec![Arc::new(int32_array)]).unwrap();
1220+
1221+
let ctx = SessionContext::new();
1222+
let table = MemTable::try_new(batch.schema(), vec![vec![batch]]).unwrap();
1223+
ctx.register_table("t", Arc::new(table)).unwrap();
1224+
1225+
{
1226+
let sql = "select \"int32\" from t";
1227+
let plan = ctx.create_logical_plan(sql);
1228+
assert!(plan.is_err());
1229+
}
1230+
1231+
{
1232+
let sql = "select \"INT32\" from t";
1233+
let actual = execute_to_batches(&ctx, sql).await;
1234+
1235+
let expected = vec![
1236+
"+-------+",
1237+
"| INT32 |",
1238+
"+-------+",
1239+
"| 1 |",
1240+
"| 2 |",
1241+
"| 3 |",
1242+
"| 4 |",
1243+
"| 5 |",
1244+
"+-------+",
1245+
];
1246+
assert_batches_eq!(expected, &actual);
1247+
}
1248+
}

datafusion/sql/src/planner.rs

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ fn plan_key(key: SQLExpr) -> Result<ScalarValue> {
9898
SQLExpr::Value(Value::Number(s, _)) => {
9999
ScalarValue::Int64(Some(s.parse().unwrap()))
100100
}
101-
SQLExpr::Value(Value::SingleQuotedString(s)) => ScalarValue::Utf8(Some(s)),
101+
SQLExpr::Value(Value::SingleQuotedString(s) | Value::DoubleQuotedString(s)) => {
102+
ScalarValue::Utf8(Some(s))
103+
}
102104
_ => {
103105
return Err(DataFusionError::SQL(ParserError(format!(
104106
"Unsuported index key expression: {:?}",
@@ -1596,7 +1598,9 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
15961598
row.into_iter()
15971599
.map(|v| match v {
15981600
SQLExpr::Value(Value::Number(n, _)) => parse_sql_number(&n),
1599-
SQLExpr::Value(Value::SingleQuotedString(s)) => Ok(lit(s)),
1601+
SQLExpr::Value(
1602+
Value::SingleQuotedString(s) | Value::DoubleQuotedString(s),
1603+
) => Ok(lit(s)),
16001604
SQLExpr::Value(Value::Null) => {
16011605
Ok(Expr::Literal(ScalarValue::Null))
16021606
}
@@ -1638,7 +1642,7 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
16381642
) -> Result<Expr> {
16391643
match sql {
16401644
SQLExpr::Value(Value::Number(n, _)) => parse_sql_number(&n),
1641-
SQLExpr::Value(Value::SingleQuotedString(ref s)) => Ok(lit(s.clone())),
1645+
SQLExpr::Value(Value::SingleQuotedString(ref s) | Value::DoubleQuotedString(ref s)) => Ok(lit(s.clone())),
16421646
SQLExpr::Value(Value::Boolean(n)) => Ok(lit(n)),
16431647
SQLExpr::Value(Value::Null) => Ok(Expr::Literal(ScalarValue::Null)),
16441648
SQLExpr::Extract { field, expr } => Ok(Expr::ScalarFunction {
@@ -2219,7 +2223,9 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
22192223

22202224
// Only handle string exprs for now
22212225
let value = match value {
2222-
SQLExpr::Value(Value::SingleQuotedString(s)) => s,
2226+
SQLExpr::Value(
2227+
Value::SingleQuotedString(s) | Value::DoubleQuotedString(s),
2228+
) => s,
22232229
_ => {
22242230
return Err(DataFusionError::NotImplemented(format!(
22252231
"Unsupported interval argument. Expected string literal, got: {:?}",
@@ -2595,6 +2601,7 @@ fn parse_sql_number(n: &str) -> Result<Expr> {
25952601
mod tests {
25962602
use super::*;
25972603
use crate::assert_contains;
2604+
use sqlparser::dialect::{Dialect, GenericDialect, MySqlDialect};
25982605
use std::any::Any;
25992606

26002607
#[test]
@@ -4371,8 +4378,16 @@ mod tests {
43714378
}
43724379

43734380
fn logical_plan(sql: &str) -> Result<LogicalPlan> {
4381+
let dialect = &GenericDialect {};
4382+
logical_plan_with_dialect(sql, dialect)
4383+
}
4384+
4385+
fn logical_plan_with_dialect(
4386+
sql: &str,
4387+
dialect: &dyn Dialect,
4388+
) -> Result<LogicalPlan> {
43744389
let planner = SqlToRel::new(&MockContextProvider {});
4375-
let result = DFParser::parse_sql(sql);
4390+
let result = DFParser::parse_sql_with_dialect(sql, dialect);
43764391
let mut ast = result?;
43774392
planner.statement_to_plan(ast.pop_front().unwrap())
43784393
}
@@ -4840,6 +4855,24 @@ mod tests {
48404855
quick_test(sql, expected);
48414856
}
48424857

4858+
#[test]
4859+
fn test_double_quoted_literal_string() {
4860+
// Assert double quoted literal string is parsed correctly like single quoted one in specific dialect.
4861+
let dialect = &MySqlDialect {};
4862+
let single_quoted_res = format!(
4863+
"{:?}",
4864+
logical_plan_with_dialect("SELECT '1'", dialect).unwrap()
4865+
);
4866+
let double_quoted_res = format!(
4867+
"{:?}",
4868+
logical_plan_with_dialect("SELECT \"1\"", dialect).unwrap()
4869+
);
4870+
assert_eq!(single_quoted_res, double_quoted_res);
4871+
4872+
// It should return error in other dialect.
4873+
assert!(logical_plan("SELECT \"1\"").is_err());
4874+
}
4875+
48434876
fn assert_field_not_found(err: DataFusionError, name: &str) {
48444877
match err {
48454878
DataFusionError::SchemaError { .. } => {

0 commit comments

Comments
 (0)