blob: 24bb813634cc108f24b28caaeef03333e49ecb65 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use crate::planner::{ContextProvider, PlannerContext, SqlToRel};
use datafusion_common::{plan_err, DFSchema, Diagnostic, Result, Span, Spans};
use datafusion_expr::expr::{Exists, InSubquery};
use datafusion_expr::{Expr, LogicalPlan, Subquery};
use sqlparser::ast::Expr as SQLExpr;
use sqlparser::ast::{Query, SelectItem, SetExpr};
use std::sync::Arc;
impl<S: ContextProvider> SqlToRel<'_, S> {
pub(super) fn parse_exists_subquery(
&self,
subquery: Query,
negated: bool,
input_schema: &DFSchema,
planner_context: &mut PlannerContext,
) -> Result<Expr> {
let old_outer_query_schema =
planner_context.set_outer_query_schema(Some(input_schema.clone().into()));
let sub_plan = self.query_to_plan(subquery, planner_context)?;
let outer_ref_columns = sub_plan.all_out_ref_exprs();
planner_context.set_outer_query_schema(old_outer_query_schema);
Ok(Expr::Exists(Exists {
subquery: Subquery {
subquery: Arc::new(sub_plan),
outer_ref_columns,
spans: Spans::new(),
},
negated,
}))
}
pub(super) fn parse_in_subquery(
&self,
expr: SQLExpr,
subquery: Query,
negated: bool,
input_schema: &DFSchema,
planner_context: &mut PlannerContext,
) -> Result<Expr> {
let old_outer_query_schema =
planner_context.set_outer_query_schema(Some(input_schema.clone().into()));
let mut spans = Spans::new();
if let SetExpr::Select(select) = &subquery.body.as_ref() {
for item in &select.projection {
if let SelectItem::UnnamedExpr(SQLExpr::Identifier(ident)) = item {
if let Some(span) = Span::try_from_sqlparser_span(ident.span) {
spans.add_span(span);
}
}
}
}
let sub_plan = self.query_to_plan(subquery, planner_context)?;
let outer_ref_columns = sub_plan.all_out_ref_exprs();
planner_context.set_outer_query_schema(old_outer_query_schema);
self.validate_single_column(
&sub_plan,
spans.clone(),
"Too many columns! The subquery should only return one column",
"Select only one column in the subquery",
)?;
let expr_obj = self.sql_to_expr(expr, input_schema, planner_context)?;
Ok(Expr::InSubquery(InSubquery::new(
Box::new(expr_obj),
Subquery {
subquery: Arc::new(sub_plan),
outer_ref_columns,
spans,
},
negated,
)))
}
pub(super) fn parse_scalar_subquery(
&self,
subquery: Query,
input_schema: &DFSchema,
planner_context: &mut PlannerContext,
) -> Result<Expr> {
let old_outer_query_schema =
planner_context.set_outer_query_schema(Some(input_schema.clone().into()));
let mut spans = Spans::new();
if let SetExpr::Select(select) = subquery.body.as_ref() {
for item in &select.projection {
if let SelectItem::ExprWithAlias { alias, .. } = item {
if let Some(span) = Span::try_from_sqlparser_span(alias.span) {
spans.add_span(span);
}
}
}
}
let sub_plan = self.query_to_plan(subquery, planner_context)?;
let outer_ref_columns = sub_plan.all_out_ref_exprs();
planner_context.set_outer_query_schema(old_outer_query_schema);
self.validate_single_column(
&sub_plan,
spans.clone(),
"Too many columns! The subquery should only return one column",
"Select only one column in the subquery",
)?;
Ok(Expr::ScalarSubquery(Subquery {
subquery: Arc::new(sub_plan),
outer_ref_columns,
spans,
}))
}
fn validate_single_column(
&self,
sub_plan: &LogicalPlan,
spans: Spans,
error_message: &str,
help_message: &str,
) -> Result<()> {
if sub_plan.schema().fields().len() > 1 {
let sub_schema = sub_plan.schema();
let field_names = sub_schema.field_names();
let diagnostic =
self.build_multi_column_diagnostic(spans, error_message, help_message);
plan_err!("{}: {}", error_message, field_names.join(", "); diagnostic=diagnostic)
} else {
Ok(())
}
}
fn build_multi_column_diagnostic(
&self,
spans: Spans,
error_message: &str,
help_message: &str,
) -> Diagnostic {
let full_span = Span::union_iter(spans.0.iter().cloned());
let mut diagnostic = Diagnostic::new_error(error_message, full_span);
for (i, span) in spans.iter().skip(1).enumerate() {
diagnostic.add_note(format!("Extra column {}", i + 1), Some(*span));
}
diagnostic.add_help(help_message, None);
diagnostic
}
}