Skip to content
8 changes: 8 additions & 0 deletions datafusion-common/src/dfschema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,14 @@ impl DFSchema {
}
}

/// Find all fields having the given qualifier
pub fn fields_with_qualified(&self, qualifier: &str) -> Vec<&DFField> {
self.fields
.iter()
.filter(|field| field.qualifier().map(|q| q.eq(qualifier)).unwrap_or(false))
.collect()
}

/// Find all fields match the given name
pub fn fields_with_unqualified_name(&self, name: &str) -> Vec<&DFField> {
self.fields
Expand Down
6 changes: 6 additions & 0 deletions datafusion-expr/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ pub enum Expr {
},
/// Represents a reference to all fields in a schema.
Wildcard,
/// Represents a reference to all fields in a specific schema.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally have a slight preference for a single Expr::Wildcard with optional qualifier

  Wildcard { qualifier: Option<String>}

but that is a personal preference and there is nothing wrong with this approach

QualifiedWildcard { qualifier: String },
}

/// Fixed seed for the hashing so that Ords are consistent across runs
Expand Down Expand Up @@ -512,6 +514,7 @@ impl fmt::Debug for Expr {
}
}
Expr::Wildcard => write!(f, "*"),
Expr::QualifiedWildcard { qualifier } => write!(f, "{}.*", qualifier),
Expr::GetIndexedField { ref expr, key } => {
write!(f, "({:?})[{}]", expr, key)
}
Expand Down Expand Up @@ -696,6 +699,9 @@ fn create_name(e: &Expr, input_schema: &DFSchema) -> Result<String> {
Expr::Wildcard => Err(DataFusionError::Internal(
"Create name does not support wildcard".to_string(),
)),
Expr::QualifiedWildcard { .. } => Err(DataFusionError::Internal(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can create a name for QualifiedWildcard?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree including the name of the qualifier in the message might be helpful

"Create name does not support qualified wildcard".to_string(),
)),
}
}

Expand Down
3 changes: 2 additions & 1 deletion datafusion/src/datasource/listing/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ impl ExpressionVisitor for ApplicabilityVisitor<'_> {
| Expr::AggregateFunction { .. }
| Expr::Sort { .. }
| Expr::WindowFunction { .. }
| Expr::Wildcard => {
| Expr::Wildcard
| Expr::QualifiedWildcard { .. } => {
*self.is_applicable = false;
Recursion::Stop(self)
}
Expand Down
23 changes: 23 additions & 0 deletions datafusion/src/logical_plan/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,8 @@ pub fn project_with_alias(
Expr::Wildcard => {
projected_expr.extend(expand_wildcard(input_schema, &plan)?)
}
Expr::QualifiedWildcard { ref qualifier } => projected_expr
.extend(expand_qualified_wildcard(qualifier, input_schema, &plan)?),
_ => projected_expr
.push(columnize_expr(normalize_col(e, &plan)?, input_schema)),
}
Expand Down Expand Up @@ -1090,6 +1092,27 @@ pub(crate) fn expand_wildcard(
}
}

pub(crate) fn expand_qualified_wildcard(
qualifier: &str,
schema: &DFSchema,
plan: &LogicalPlan,
) -> Result<Vec<Expr>> {
let qualified_fields: Vec<DFField> = schema
.fields_with_qualified(qualifier)
.into_iter()
.cloned()
.collect();
if qualified_fields.is_empty() {
return Err(DataFusionError::Plan(format!(
"Invalid qualifier {}",
qualifier
)));
}
let qualifier_schema =
DFSchema::new_with_metadata(qualified_fields, schema.metadata().clone())?;
expand_wildcard(&qualifier_schema, plan)
}

#[cfg(test)]
mod tests {
use arrow::datatypes::{DataType, Field};
Expand Down
3 changes: 3 additions & 0 deletions datafusion/src/logical_plan/expr_rewriter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,9 @@ impl ExprRewritable for Expr {
negated,
},
Expr::Wildcard => Expr::Wildcard,
Expr::QualifiedWildcard { qualifier } => {
Expr::QualifiedWildcard { qualifier }
}
Expr::GetIndexedField { expr, key } => Expr::GetIndexedField {
expr: rewrite_boxed(expr, rewriter)?,
key,
Expand Down
8 changes: 8 additions & 0 deletions datafusion/src/logical_plan/expr_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ impl ExprSchemable for Expr {
Expr::Wildcard => Err(DataFusionError::Internal(
"Wildcard expressions are not valid in a logical query plan".to_owned(),
)),
Expr::QualifiedWildcard { .. } => Err(DataFusionError::Internal(
"QualifiedWildcard expressions are not valid in a logical query plan"
.to_owned(),
)),
Expr::GetIndexedField { ref expr, key } => {
let data_type = expr.get_type(schema)?;

Expand Down Expand Up @@ -178,6 +182,10 @@ impl ExprSchemable for Expr {
Expr::Wildcard => Err(DataFusionError::Internal(
"Wildcard expressions are not valid in a logical query plan".to_owned(),
)),
Expr::QualifiedWildcard { .. } => Err(DataFusionError::Internal(
"QualifiedWildcard expressions are not valid in a logical query plan"
.to_owned(),
)),
Expr::GetIndexedField { ref expr, key } => {
let data_type = expr.get_type(input_schema)?;
get_indexed_field(&data_type, key).map(|x| x.is_nullable())
Expand Down
3 changes: 2 additions & 1 deletion datafusion/src/logical_plan/expr_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ impl ExprVisitable for Expr {
Expr::Column(_)
| Expr::ScalarVariable(_, _)
| Expr::Literal(_)
| Expr::Wildcard => Ok(visitor),
| Expr::Wildcard
| Expr::QualifiedWildcard { .. } => Ok(visitor),
Expr::BinaryExpr { left, right, .. } => {
let visitor = left.accept(visitor)?;
right.accept(visitor)
Expand Down
4 changes: 4 additions & 0 deletions datafusion/src/optimizer/common_subexpr_eliminate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,10 @@ impl ExprIdentifierVisitor<'_> {
Expr::Wildcard => {
desc.push_str("Wildcard-");
}
Expr::QualifiedWildcard { qualifier } => {
desc.push_str("QualifiedWildcard-");
desc.push_str(qualifier);
}
Expr::GetIndexedField { key, .. } => {
desc.push_str("GetIndexedField-");
desc.push_str(&key.to_string());
Expand Down
3 changes: 2 additions & 1 deletion datafusion/src/optimizer/simplify_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,8 @@ impl<'a> ConstEvaluator<'a> {
| Expr::Column(_)
| Expr::WindowFunction { .. }
| Expr::Sort { .. }
| Expr::Wildcard => false,
| Expr::Wildcard
| Expr::QualifiedWildcard { .. } => false,
Expr::ScalarFunction { fun, .. } => Self::volatility_ok(fun.volatility()),
Expr::ScalarUDF { fun, .. } => Self::volatility_ok(fun.signature.volatility),
Expr::Literal(_)
Expand Down
11 changes: 10 additions & 1 deletion datafusion/src/optimizer/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ impl ExpressionVisitor for ColumnNameVisitor<'_> {
| Expr::AggregateUDF { .. }
| Expr::InList { .. }
| Expr::Wildcard
| Expr::QualifiedWildcard { .. }
| Expr::GetIndexedField { .. } => {}
}
Ok(Recursion::Continue(self))
Expand Down Expand Up @@ -350,6 +351,10 @@ pub fn expr_sub_expressions(expr: &Expr) -> Result<Vec<Expr>> {
Expr::Wildcard { .. } => Err(DataFusionError::Internal(
"Wildcard expressions are not valid in a logical query plan".to_owned(),
)),
Expr::QualifiedWildcard { .. } => Err(DataFusionError::Internal(
"QualifiedWildcard expressions are not valid in a logical query plan"
.to_owned(),
)),
}
}

Expand Down Expand Up @@ -506,9 +511,13 @@ pub fn rewrite_expression(expr: &Expr, expressions: &[Expr]) -> Result<Expr> {
Ok(expr)
}
}
Expr::Wildcard { .. } => Err(DataFusionError::Internal(
Expr::Wildcard => Err(DataFusionError::Internal(
"Wildcard expressions are not valid in a logical query plan".to_owned(),
)),
Expr::QualifiedWildcard { .. } => Err(DataFusionError::Internal(
"QualifiedWildcard expressions are not valid in a logical query plan"
.to_owned(),
)),
Expr::GetIndexedField { expr: _, key } => Ok(Expr::GetIndexedField {
expr: Box::new(expressions[0].clone()),
key: key.clone(),
Expand Down
3 changes: 3 additions & 0 deletions datafusion/src/physical_plan/planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ fn create_physical_name(e: &Expr, is_first_expr: bool) -> Result<String> {
Expr::Wildcard => Err(DataFusionError::Internal(
"Create physical name does not support wildcard".to_string(),
)),
Expr::QualifiedWildcard { .. } => Err(DataFusionError::Internal(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

"Create physical name does not support qualified wildcard".to_string(),
)),
}
}

Expand Down
19 changes: 12 additions & 7 deletions datafusion/src/sql/planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ use crate::datasource::TableProvider;
use crate::logical_plan::window_frames::{WindowFrame, WindowFrameUnits};
use crate::logical_plan::Expr::Alias;
use crate::logical_plan::{
and, builder::expand_wildcard, col, lit, normalize_col, union_with_alias, Column,
CreateCatalogSchema, CreateExternalTable as PlanCreateExternalTable,
CreateMemoryTable, DFSchema, DFSchemaRef, DropTable, Expr, LogicalPlan,
LogicalPlanBuilder, Operator, PlanType, ToDFSchema, ToStringifiedPlan,
and, builder::expand_qualified_wildcard, builder::expand_wildcard, col, lit,
normalize_col, union_with_alias, Column, CreateCatalogSchema,
CreateExternalTable as PlanCreateExternalTable, CreateMemoryTable, DFSchema,
DFSchemaRef, DropTable, Expr, LogicalPlan, LogicalPlanBuilder, Operator, PlanType,
ToDFSchema, ToStringifiedPlan,
};
use crate::optimizer::utils::exprlist_to_columns;
use crate::prelude::JoinType;
Expand Down Expand Up @@ -1010,6 +1011,9 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
}
expand_wildcard(input_schema, plan)?
}
Expr::QualifiedWildcard { ref qualifier } => {
expand_qualified_wildcard(qualifier, input_schema, plan)?
}
_ => vec![normalize_col(expr, plan)?],
})
})
Expand Down Expand Up @@ -1210,9 +1214,10 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
normalize_ident(alias),
)),
SelectItem::Wildcard => Ok(Expr::Wildcard),
SelectItem::QualifiedWildcard(_) => Err(DataFusionError::NotImplemented(
"Qualified wildcards are not supported".to_string(),
)),
SelectItem::QualifiedWildcard(ref object_name) => {
let qualifier = format!("{}", object_name);
Ok(Expr::QualifiedWildcard { qualifier })
}
}
}

Expand Down
1 change: 1 addition & 0 deletions datafusion/src/sql/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ where
Ok(expr.clone())
}
Expr::Wildcard => Ok(Expr::Wildcard),
Expr::QualifiedWildcard { .. } => Ok(expr.clone()),
Expr::GetIndexedField { expr, key } => Ok(Expr::GetIndexedField {
expr: Box::new(clone_with_replacement(expr.as_ref(), replacement_fn)?),
key: key.clone(),
Expand Down
1 change: 1 addition & 0 deletions datafusion/tests/sql/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ pub mod select;
pub mod timestamp;
pub mod udf;
pub mod union;
pub mod wildcard;
pub mod window;

mod explain;
Expand Down
Loading