Skip to content

Commit a3ef9a2

Browse files
HuSen8891leo
authored and
leo
committed
Fix: check ambiguous column reference
1 parent 6590ea3 commit a3ef9a2

File tree

3 files changed

+91
-6
lines changed

3 files changed

+91
-6
lines changed

datafusion/common/src/dfschema.rs

+60-4
Original file line numberDiff line numberDiff line change
@@ -419,10 +419,7 @@ impl DFSchema {
419419
name: &str,
420420
) -> Result<(Option<&TableReference>, &Field)> {
421421
if let Some(qualifier) = qualifier {
422-
let idx = self
423-
.index_of_column_by_name(Some(qualifier), name)
424-
.ok_or_else(|| field_not_found(Some(qualifier.clone()), name, self))?;
425-
Ok((self.field_qualifiers[idx].as_ref(), self.field(idx)))
422+
self.qualified_field_with_qualified_name(qualifier, name)
426423
} else {
427424
self.qualified_field_with_unqualified_name(name)
428425
}
@@ -467,6 +464,36 @@ impl DFSchema {
467464
.collect()
468465
}
469466

467+
/// Find all fields that match the given name with qualifier and return them with their qualifier
468+
pub fn qualified_fields_with_qualified_name(
469+
&self,
470+
qualifier: &TableReference,
471+
name: &str,
472+
) -> Vec<(Option<&TableReference>, &Field)> {
473+
self.iter()
474+
.filter(|(q, f)| match (qualifier, q) {
475+
// field to lookup is qualified.
476+
// current field is qualified and not shared between relations, compare both
477+
// qualifier and name.
478+
(q, Some(field_q)) => q.resolved_eq(field_q) && f.name() == name,
479+
// field to lookup is qualified but current field is unqualified.
480+
(qq, None) => {
481+
// the original field may now be aliased with a name that matches the
482+
// original qualified name
483+
let column = Column::from_qualified_name(f.name());
484+
match column {
485+
Column {
486+
relation: Some(r),
487+
name: column_name,
488+
} => &r == qq && column_name == name,
489+
_ => false,
490+
}
491+
}
492+
})
493+
.map(|(qualifier, field)| (qualifier, field.as_ref()))
494+
.collect()
495+
}
496+
470497
/// Find all fields that match the given name and convert to column
471498
pub fn columns_with_unqualified_name(&self, name: &str) -> Vec<Column> {
472499
self.iter()
@@ -519,6 +546,35 @@ impl DFSchema {
519546
}
520547
}
521548

549+
/// Find the qualified field with the given qualified name
550+
pub fn qualified_field_with_qualified_name(
551+
&self,
552+
qualifier: &TableReference,
553+
name: &str,
554+
) -> Result<(Option<&TableReference>, &Field)> {
555+
let matches = self.qualified_fields_with_qualified_name(qualifier, name);
556+
match matches.len() {
557+
0 => Err(field_not_found(Some(qualifier.clone()), name, self)),
558+
1 => Ok((matches[0].0, (matches[0].1))),
559+
_ => {
560+
let fields_with_qualifier = matches
561+
.iter()
562+
.filter(|(q, _)| q.is_some())
563+
.collect::<Vec<_>>();
564+
if fields_with_qualifier.len() == 1 {
565+
Ok((fields_with_qualifier[0].0, fields_with_qualifier[0].1))
566+
} else {
567+
_schema_err!(SchemaError::AmbiguousReference {
568+
field: Column {
569+
relation: None,
570+
name: name.to_string(),
571+
},
572+
})
573+
}
574+
}
575+
}
576+
}
577+
522578
/// Find the field with the given name
523579
pub fn field_with_unqualified_name(&self, name: &str) -> Result<&Field> {
524580
self.qualified_field_with_unqualified_name(name)

datafusion/sql/src/expr/identifier.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use sqlparser::ast::{Expr as SQLExpr, Ident};
2020

2121
use datafusion_common::{
2222
internal_err, not_impl_err, plan_datafusion_err, Column, DFSchema, DataFusionError,
23-
Result, TableReference,
23+
Result, SchemaError, TableReference,
2424
};
2525
use datafusion_expr::planner::PlannerResult;
2626
use datafusion_expr::{Case, Expr};
@@ -186,7 +186,22 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
186186
let s = &ids[0..ids.len()];
187187
// safe unwrap as s can never be empty or exceed the bounds
188188
let (relation, column_name) = form_identifier(s).unwrap();
189-
Ok(Expr::Column(Column::new(relation, column_name)))
189+
// sanity check on column
190+
match schema
191+
.qualified_field_with_name(relation.as_ref(), column_name)
192+
{
193+
Err(e) => match e {
194+
DataFusionError::SchemaError(
195+
SchemaError::AmbiguousReference { .. },
196+
_,
197+
) => Err(e),
198+
_ => Ok(Expr::Column(Column::new(
199+
relation,
200+
column_name,
201+
))),
202+
},
203+
_ => Ok(Expr::Column(Column::new(relation, column_name))),
204+
}
190205
}
191206
}
192207
}

datafusion/sqllogictest/test_files/join.slt

+14
Original file line numberDiff line numberDiff line change
@@ -1209,3 +1209,17 @@ drop table t1;
12091209

12101210
statement ok
12111211
drop table t2;
1212+
1213+
# Test SQLancer issue: https://github.com/apache/datafusion/issues/12337
1214+
statement ok
1215+
create table t1(v1 int);
1216+
1217+
## Query with Ambiguous column reference
1218+
query error DataFusion error: Schema error: Ambiguous reference to unqualified field v1
1219+
select count(*)
1220+
from t1
1221+
right outer join t1
1222+
on t1.v1 > 0;
1223+
1224+
statement ok
1225+
drop table t1;

0 commit comments

Comments
 (0)