This is an automated email from the ASF dual-hosted git repository.
jayzhan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git
The following commit(s) were added to refs/heads/main by this push:
new 359735ae17 Expand wildcard to actual expressions in
`prepare_select_exprs` (#15090)
359735ae17 is described below
commit 359735ae1796ad28dbaae451249f0ecd24f38d28
Author: Jay Zhan <[email protected]>
AuthorDate: Wed Mar 12 07:49:43 2025 +0800
Expand wildcard to actual expressions in `prepare_select_exprs` (#15090)
* first draft
* fix tests
* cleanup
* assert
* clippy
* fix test
* fix test
---
.../optimizer/tests/optimizer_integration.rs | 40 +-
datafusion/sql/src/select.rs | 78 ++-
datafusion/sql/tests/cases/plan_to_sql.rs | 57 +--
datafusion/sql/tests/sql_integration.rs | 525 ---------------------
datafusion/sqllogictest/test_files/alias.slt | 59 +++
datafusion/sqllogictest/test_files/copy.slt | 17 +
datafusion/sqllogictest/test_files/cte.slt | 146 ++++++
datafusion/sqllogictest/test_files/group_by.slt | 29 ++
datafusion/sqllogictest/test_files/joins.slt | 266 +++++++++++
datafusion/sqllogictest/test_files/prepare.slt | 15 +
datafusion/sqllogictest/test_files/select.slt | 13 +
datafusion/sqllogictest/test_files/subquery.slt | 20 +
datafusion/sqllogictest/test_files/wildcard.slt | 15 +
13 files changed, 673 insertions(+), 607 deletions(-)
diff --git a/datafusion/optimizer/tests/optimizer_integration.rs
b/datafusion/optimizer/tests/optimizer_integration.rs
index 66bd6b7512..5e66c7ec03 100644
--- a/datafusion/optimizer/tests/optimizer_integration.rs
+++ b/datafusion/optimizer/tests/optimizer_integration.rs
@@ -22,16 +22,14 @@ use std::sync::Arc;
use arrow::datatypes::{DataType, Field, Schema, SchemaRef, TimeUnit};
use datafusion_common::config::ConfigOptions;
-use datafusion_common::{assert_contains, plan_err, Result, TableReference};
+use datafusion_common::{plan_err, Result, TableReference};
use datafusion_expr::planner::ExprPlanner;
-use datafusion_expr::sqlparser::dialect::PostgreSqlDialect;
use datafusion_expr::test::function_stub::sum_udaf;
use datafusion_expr::{AggregateUDF, LogicalPlan, ScalarUDF, TableSource,
WindowUDF};
use datafusion_functions_aggregate::average::avg_udaf;
use datafusion_functions_aggregate::count::count_udaf;
use datafusion_functions_aggregate::planner::AggregateFunctionPlanner;
use datafusion_functions_window::planner::WindowFunctionPlanner;
-use datafusion_optimizer::analyzer::type_coercion::TypeCoercionRewriter;
use datafusion_optimizer::analyzer::Analyzer;
use datafusion_optimizer::optimizer::Optimizer;
use datafusion_optimizer::{OptimizerConfig, OptimizerContext, OptimizerRule};
@@ -344,16 +342,6 @@ fn test_propagate_empty_relation_inner_join_and_unions() {
assert_eq!(expected, format!("{plan}"));
}
-#[test]
-fn select_wildcard_with_repeated_column() {
- let sql = "SELECT *, col_int32 FROM test";
- let err = test_sql(sql).expect_err("query should have failed");
- assert_eq!(
- "Schema error: Schema contains duplicate qualified field name
test.col_int32",
- err.strip_backtrace()
- );
-}
-
#[test]
fn select_wildcard_with_repeated_column_but_is_aliased() {
let sql = "SELECT *, col_int32 as col_32 FROM test";
@@ -390,32 +378,6 @@ fn
select_correlated_predicate_subquery_with_uppercase_ident() {
assert_eq!(expected, format!("{plan}"));
}
-// The test should return an error
-// because the wildcard didn't be expanded before type coercion
-#[test]
-fn test_union_coercion_with_wildcard() -> Result<()> {
- let dialect = PostgreSqlDialect {};
- let context_provider = MyContextProvider::default();
- let sql = "select * from (SELECT col_int32, col_uint32 FROM test) union
all select * from(SELECT col_uint32, col_int32 FROM test)";
- let statements = Parser::parse_sql(&dialect, sql)?;
- let sql_to_rel = SqlToRel::new(&context_provider);
- let logical_plan =
sql_to_rel.sql_statement_to_plan(statements[0].clone())?;
-
- if let LogicalPlan::Union(union) = logical_plan {
- let err = TypeCoercionRewriter::coerce_union(union)
- .err()
- .unwrap()
- .to_string();
- assert_contains!(
- err,
- "Error during planning: Wildcard should be expanded before type
coercion"
- );
- } else {
- panic!("Expected Union plan");
- }
- Ok(())
-}
-
fn test_sql(sql: &str) -> Result<LogicalPlan> {
// parse the SQL
let dialect = GenericDialect {}; // or AnsiDialect, or your own dialect ...
diff --git a/datafusion/sql/src/select.rs b/datafusion/sql/src/select.rs
index e21def4c39..ce9c5d2f7c 100644
--- a/datafusion/sql/src/select.rs
+++ b/datafusion/sql/src/select.rs
@@ -27,19 +27,19 @@ use crate::utils::{
use datafusion_common::error::DataFusionErrorBuilder;
use datafusion_common::tree_node::{TreeNode, TreeNodeRecursion};
-use datafusion_common::{not_impl_err, plan_err, Result};
+use datafusion_common::{not_impl_err, plan_err, Column, Result};
use datafusion_common::{RecursionUnnestOption, UnnestOptions};
use datafusion_expr::expr::{Alias, PlannedReplaceSelectItem, WildcardOptions};
use datafusion_expr::expr_rewriter::{
normalize_col, normalize_col_with_schemas_and_ambiguity_check,
normalize_sorts,
};
use datafusion_expr::utils::{
- expr_as_column_expr, expr_to_columns, find_aggregate_exprs,
find_window_exprs,
+ expand_qualified_wildcard, expand_wildcard, expr_as_column_expr,
expr_to_columns,
+ find_aggregate_exprs, find_window_exprs,
};
use datafusion_expr::{
- qualified_wildcard_with_options, wildcard_with_options, Aggregate, Expr,
Filter,
- GroupingSet, LogicalPlan, LogicalPlanBuilder, LogicalPlanBuilderOptions,
- Partitioning,
+ Aggregate, Expr, Filter, GroupingSet, LogicalPlan, LogicalPlanBuilder,
+ LogicalPlanBuilderOptions, Partitioning,
};
use indexmap::IndexMap;
@@ -92,6 +92,12 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
planner_context,
)?;
+ // TOOD: remove this after Expr::Wildcard is removed
+ #[allow(deprecated)]
+ for expr in &select_exprs {
+ debug_assert!(!matches!(expr, Expr::Wildcard { .. }));
+ }
+
// Having and group by clause may reference aliases defined in select
projection
let projected_plan = self.project(base_plan.clone(),
select_exprs.clone())?;
@@ -583,7 +589,7 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
let mut error_builder = DataFusionErrorBuilder::new();
for expr in projection {
match self.sql_select_to_rex(expr, plan, empty_from,
planner_context) {
- Ok(expr) => prepared_select_exprs.push(expr),
+ Ok(expr) => prepared_select_exprs.extend(expr),
Err(err) => error_builder.add_error(err),
}
}
@@ -597,7 +603,7 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
plan: &LogicalPlan,
empty_from: bool,
planner_context: &mut PlannerContext,
- ) -> Result<Expr> {
+ ) -> Result<Vec<Expr>> {
match sql {
SelectItem::UnnamedExpr(expr) => {
let expr = self.sql_to_expr(expr, plan.schema(),
planner_context)?;
@@ -606,7 +612,7 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
&[&[plan.schema()]],
&plan.using_columns()?,
)?;
- Ok(col)
+ Ok(vec![col])
}
SelectItem::ExprWithAlias { expr, alias } => {
let select_expr =
@@ -622,7 +628,7 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
Expr::Column(column) if column.name.eq(&name) => col,
_ => col.alias(name),
};
- Ok(expr)
+ Ok(vec![expr])
}
SelectItem::Wildcard(options) => {
Self::check_wildcard_options(&options)?;
@@ -635,7 +641,17 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
planner_context,
options,
)?;
- Ok(wildcard_with_options(planned_options))
+
+ let expanded =
+ expand_wildcard(plan.schema(), plan,
Some(&planned_options))?;
+
+ // If there is a REPLACE statement, replace that column with
the given
+ // replace expression. Column name remains the same.
+ if let Some(replace) = planned_options.replace {
+ replace_columns(expanded, &replace)
+ } else {
+ Ok(expanded)
+ }
}
SelectItem::QualifiedWildcard(object_name, options) => {
Self::check_wildcard_options(&options)?;
@@ -646,7 +662,19 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
planner_context,
options,
)?;
- Ok(qualified_wildcard_with_options(qualifier, planned_options))
+
+ let expanded = expand_qualified_wildcard(
+ &qualifier,
+ plan.schema(),
+ Some(&planned_options),
+ )?;
+ // If there is a REPLACE statement, replace that column with
the given
+ // replace expression. Column name remains the same.
+ if let Some(replace) = planned_options.replace {
+ replace_columns(expanded, &replace)
+ } else {
+ Ok(expanded)
+ }
}
}
}
@@ -698,7 +726,10 @@ impl<S: ContextProvider> SqlToRel<'_, S> {
planner_context,
)
})
- .collect::<Result<Vec<_>>>()?;
+ .collect::<Result<Vec<_>>>()?
+ .into_iter()
+ .flatten()
+ .collect();
let planned_replace = PlannedReplaceSelectItem {
items: replace.items.into_iter().map(|i| *i).collect(),
planned_expressions: replace_expr,
@@ -884,3 +915,26 @@ fn match_window_definitions(
}
Ok(())
}
+
+/// If there is a REPLACE statement in the projected expression in the form of
+/// "REPLACE (some_column_within_an_expr AS some_column)", this function
replaces
+/// that column with the given replace expression. Column name remains the
same.
+/// Multiple REPLACEs are also possible with comma separations.
+fn replace_columns(
+ mut exprs: Vec<Expr>,
+ replace: &PlannedReplaceSelectItem,
+) -> Result<Vec<Expr>> {
+ for expr in exprs.iter_mut() {
+ if let Expr::Column(Column { name, .. }) = expr {
+ if let Some((_, new_expr)) = replace
+ .items()
+ .iter()
+ .zip(replace.expressions().iter())
+ .find(|(item, _)| item.column_name.value == *name)
+ {
+ *expr = new_expr.clone().alias(name.clone())
+ }
+ }
+ }
+ Ok(exprs)
+}
diff --git a/datafusion/sql/tests/cases/plan_to_sql.rs
b/datafusion/sql/tests/cases/plan_to_sql.rs
index fc0b7a26ba..8599de8646 100644
--- a/datafusion/sql/tests/cases/plan_to_sql.rs
+++ b/datafusion/sql/tests/cases/plan_to_sql.rs
@@ -325,9 +325,9 @@ fn roundtrip_statement_with_dialect() -> Result<()> {
unparser_dialect: Box::new(UnparserMySqlDialect {}),
},
TestStatementWithDialect {
- sql: "select * from (select * from j1 limit 10);",
+ sql: "select j1_id from (select j1_id from j1 limit 10);",
expected:
- "SELECT * FROM (SELECT * FROM `j1` LIMIT 10) AS
`derived_limit`",
+ "SELECT `j1`.`j1_id` FROM (SELECT `j1`.`j1_id` FROM `j1` LIMIT
10) AS `derived_limit`",
parser_dialect: Box::new(MySqlDialect {}),
unparser_dialect: Box::new(UnparserMySqlDialect {}),
},
@@ -527,85 +527,79 @@ fn roundtrip_statement_with_dialect() -> Result<()> {
},
TestStatementWithDialect {
sql: "SELECT * FROM (SELECT j1_id + 1 FROM j1) AS temp_j(id2)",
- expected: r#"SELECT * FROM (SELECT (`j1`.`j1_id` + 1) AS `id2`
FROM `j1`) AS `temp_j`"#,
+ expected: r#"SELECT `temp_j`.`id2` FROM (SELECT (`j1`.`j1_id` + 1)
AS `id2` FROM `j1`) AS `temp_j`"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect: Box::new(SqliteDialect {}),
},
TestStatementWithDialect {
sql: "SELECT * FROM (SELECT j1_id FROM j1 LIMIT 1) AS temp_j(id2)",
- expected: r#"SELECT * FROM (SELECT `j1`.`j1_id` AS `id2` FROM `j1`
LIMIT 1) AS `temp_j`"#,
+ expected: r#"SELECT `temp_j`.`id2` FROM (SELECT `j1`.`j1_id` AS
`id2` FROM `j1` LIMIT 1) AS `temp_j`"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect: Box::new(SqliteDialect {}),
},
TestStatementWithDialect {
sql: "SELECT * FROM UNNEST([1,2,3])",
- expected: r#"SELECT * FROM (SELECT UNNEST([1, 2, 3]) AS
"UNNEST(make_array(Int64(1),Int64(2),Int64(3)))")"#,
+ expected: r#"SELECT
"UNNEST(make_array(Int64(1),Int64(2),Int64(3)))" FROM (SELECT UNNEST([1, 2, 3])
AS "UNNEST(make_array(Int64(1),Int64(2),Int64(3)))")"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect: Box::new(UnparserDefaultDialect {}),
},
TestStatementWithDialect {
sql: "SELECT * FROM UNNEST([1,2,3]) AS t1 (c1)",
- expected: r#"SELECT * FROM (SELECT UNNEST([1, 2, 3]) AS
"UNNEST(make_array(Int64(1),Int64(2),Int64(3)))") AS t1 (c1)"#,
- parser_dialect: Box::new(GenericDialect {}),
- unparser_dialect: Box::new(UnparserDefaultDialect {}),
- },
- TestStatementWithDialect {
- sql: "SELECT * FROM UNNEST([1,2,3]) AS t1 (c1)",
- expected: r#"SELECT * FROM (SELECT UNNEST([1, 2, 3]) AS
"UNNEST(make_array(Int64(1),Int64(2),Int64(3)))") AS t1 (c1)"#,
+ expected: r#"SELECT t1.c1 FROM (SELECT UNNEST([1, 2, 3]) AS
"UNNEST(make_array(Int64(1),Int64(2),Int64(3)))") AS t1 (c1)"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect: Box::new(UnparserDefaultDialect {}),
},
TestStatementWithDialect {
sql: "SELECT * FROM UNNEST([1,2,3]), j1",
- expected: r#"SELECT * FROM (SELECT UNNEST([1, 2, 3]) AS
"UNNEST(make_array(Int64(1),Int64(2),Int64(3)))") CROSS JOIN j1"#,
+ expected: r#"SELECT
"UNNEST(make_array(Int64(1),Int64(2),Int64(3)))", j1.j1_id, j1.j1_string FROM
(SELECT UNNEST([1, 2, 3]) AS "UNNEST(make_array(Int64(1),Int64(2),Int64(3)))")
CROSS JOIN j1"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect: Box::new(UnparserDefaultDialect {}),
},
TestStatementWithDialect {
sql: "SELECT * FROM UNNEST([1,2,3]) u(c1) JOIN j1 ON u.c1 =
j1.j1_id",
- expected: r#"SELECT * FROM (SELECT UNNEST([1, 2, 3]) AS
"UNNEST(make_array(Int64(1),Int64(2),Int64(3)))") AS u (c1) JOIN j1 ON (u.c1 =
j1.j1_id)"#,
+ expected: r#"SELECT u.c1, j1.j1_id, j1.j1_string FROM (SELECT
UNNEST([1, 2, 3]) AS "UNNEST(make_array(Int64(1),Int64(2),Int64(3)))") AS u
(c1) JOIN j1 ON (u.c1 = j1.j1_id)"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect: Box::new(UnparserDefaultDialect {}),
},
TestStatementWithDialect {
sql: "SELECT * FROM UNNEST([1,2,3]) u(c1) UNION ALL SELECT * FROM
UNNEST([4,5,6]) u(c1)",
- expected: r#"SELECT * FROM (SELECT UNNEST([1, 2, 3]) AS
"UNNEST(make_array(Int64(1),Int64(2),Int64(3)))") AS u (c1) UNION ALL SELECT *
FROM (SELECT UNNEST([4, 5, 6]) AS
"UNNEST(make_array(Int64(4),Int64(5),Int64(6)))") AS u (c1)"#,
+ expected: r#"SELECT u.c1 FROM (SELECT UNNEST([1, 2, 3]) AS
"UNNEST(make_array(Int64(1),Int64(2),Int64(3)))") AS u (c1) UNION ALL SELECT
u.c1 FROM (SELECT UNNEST([4, 5, 6]) AS
"UNNEST(make_array(Int64(4),Int64(5),Int64(6)))") AS u (c1)"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect: Box::new(UnparserDefaultDialect {}),
},
TestStatementWithDialect {
sql: "SELECT * FROM UNNEST([1,2,3])",
- expected: r#"SELECT * FROM UNNEST([1, 2, 3])"#,
+ expected: r#"SELECT UNNEST(make_array(Int64(1),Int64(2),Int64(3)))
FROM UNNEST([1, 2, 3])"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect:
Box::new(CustomDialectBuilder::default().with_unnest_as_table_factor(true).build()),
},
TestStatementWithDialect {
sql: "SELECT * FROM UNNEST([1,2,3]) AS t1 (c1)",
- expected: r#"SELECT * FROM UNNEST([1, 2, 3]) AS t1 (c1)"#,
+ expected: r#"SELECT t1.c1 FROM UNNEST([1, 2, 3]) AS t1 (c1)"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect:
Box::new(CustomDialectBuilder::default().with_unnest_as_table_factor(true).build()),
},
TestStatementWithDialect {
sql: "SELECT * FROM UNNEST([1,2,3]) AS t1 (c1)",
- expected: r#"SELECT * FROM UNNEST([1, 2, 3]) AS t1 (c1)"#,
+ expected: r#"SELECT t1.c1 FROM UNNEST([1, 2, 3]) AS t1 (c1)"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect:
Box::new(CustomDialectBuilder::default().with_unnest_as_table_factor(true).build()),
},
TestStatementWithDialect {
sql: "SELECT * FROM UNNEST([1,2,3]), j1",
- expected: r#"SELECT * FROM UNNEST([1, 2, 3]) CROSS JOIN j1"#,
+ expected: r#"SELECT
UNNEST(make_array(Int64(1),Int64(2),Int64(3))), j1.j1_id, j1.j1_string FROM
UNNEST([1, 2, 3]) CROSS JOIN j1"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect:
Box::new(CustomDialectBuilder::default().with_unnest_as_table_factor(true).build()),
},
TestStatementWithDialect {
sql: "SELECT * FROM UNNEST([1,2,3]) u(c1) JOIN j1 ON u.c1 =
j1.j1_id",
- expected: r#"SELECT * FROM UNNEST([1, 2, 3]) AS u (c1) JOIN j1 ON
(u.c1 = j1.j1_id)"#,
+ expected: r#"SELECT u.c1, j1.j1_id, j1.j1_string FROM UNNEST([1,
2, 3]) AS u (c1) JOIN j1 ON (u.c1 = j1.j1_id)"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect:
Box::new(CustomDialectBuilder::default().with_unnest_as_table_factor(true).build()),
},
TestStatementWithDialect {
sql: "SELECT * FROM UNNEST([1,2,3]) u(c1) UNION ALL SELECT * FROM
UNNEST([4,5,6]) u(c1)",
- expected: r#"SELECT * FROM UNNEST([1, 2, 3]) AS u (c1) UNION ALL
SELECT * FROM UNNEST([4, 5, 6]) AS u (c1)"#,
+ expected: r#"SELECT u.c1 FROM UNNEST([1, 2, 3]) AS u (c1) UNION
ALL SELECT u.c1 FROM UNNEST([4, 5, 6]) AS u (c1)"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect:
Box::new(CustomDialectBuilder::default().with_unnest_as_table_factor(true).build()),
},
@@ -629,25 +623,25 @@ fn roundtrip_statement_with_dialect() -> Result<()> {
},
TestStatementWithDialect {
sql: "SELECT * FROM unnest_table u, UNNEST(u.array_col)",
- expected: r#"SELECT * FROM unnest_table AS u CROSS JOIN
UNNEST(u.array_col)"#,
+ expected: r#"SELECT u.array_col, u.struct_col,
UNNEST(outer_ref(u.array_col)) FROM unnest_table AS u CROSS JOIN
UNNEST(u.array_col)"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect:
Box::new(CustomDialectBuilder::default().with_unnest_as_table_factor(true).build()),
},
TestStatementWithDialect {
sql: "SELECT * FROM unnest_table u, UNNEST(u.array_col) AS t1
(c1)",
- expected: r#"SELECT * FROM unnest_table AS u CROSS JOIN
UNNEST(u.array_col) AS t1 (c1)"#,
+ expected: r#"SELECT u.array_col, u.struct_col, t1.c1 FROM
unnest_table AS u CROSS JOIN UNNEST(u.array_col) AS t1 (c1)"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect:
Box::new(CustomDialectBuilder::default().with_unnest_as_table_factor(true).build()),
},
TestStatementWithDialect {
sql: "SELECT * FROM unnest_table u, UNNEST(u.array_col)",
- expected: r#"SELECT * FROM unnest_table AS u CROSS JOIN LATERAL
(SELECT UNNEST(u.array_col) AS "UNNEST(outer_ref(u.array_col))")"#,
+ expected: r#"SELECT u.array_col, u.struct_col,
"UNNEST(outer_ref(u.array_col))" FROM unnest_table AS u CROSS JOIN LATERAL
(SELECT UNNEST(u.array_col) AS "UNNEST(outer_ref(u.array_col))")"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect: Box::new(UnparserDefaultDialect {}),
},
TestStatementWithDialect {
sql: "SELECT * FROM unnest_table u, UNNEST(u.array_col) AS t1
(c1)",
- expected: r#"SELECT * FROM unnest_table AS u CROSS JOIN LATERAL
(SELECT UNNEST(u.array_col) AS "UNNEST(outer_ref(u.array_col))") AS t1 (c1)"#,
+ expected: r#"SELECT u.array_col, u.struct_col, t1.c1 FROM
unnest_table AS u CROSS JOIN LATERAL (SELECT UNNEST(u.array_col) AS
"UNNEST(outer_ref(u.array_col))") AS t1 (c1)"#,
parser_dialect: Box::new(GenericDialect {}),
unparser_dialect: Box::new(UnparserDefaultDialect {}),
},
@@ -1457,13 +1451,13 @@ fn test_unnest_to_sql() {
fn test_join_with_no_conditions() {
sql_round_trip(
GenericDialect {},
- "SELECT * FROM j1 JOIN j2",
- "SELECT * FROM j1 CROSS JOIN j2",
+ "SELECT j1.j1_id, j1.j1_string FROM j1 JOIN j2",
+ "SELECT j1.j1_id, j1.j1_string FROM j1 CROSS JOIN j2",
);
sql_round_trip(
GenericDialect {},
- "SELECT * FROM j1 CROSS JOIN j2",
- "SELECT * FROM j1 CROSS JOIN j2",
+ "SELECT j1.j1_id, j1.j1_string FROM j1 CROSS JOIN j2",
+ "SELECT j1.j1_id, j1.j1_string FROM j1 CROSS JOIN j2",
);
}
@@ -1564,7 +1558,7 @@ fn test_unparse_extension_to_statement() -> Result<()> {
Arc::new(UnusedUnparser {}),
]);
let sql = unparser.plan_to_sql(&extension)?;
- let expected = "SELECT * FROM j1";
+ let expected = "SELECT j1.j1_id, j1.j1_string FROM j1";
assert_eq!(sql.to_string(), expected);
if let Some(err) = plan_to_sql(&extension).err() {
@@ -1627,7 +1621,8 @@ fn test_unparse_extension_to_sql() -> Result<()> {
Arc::new(UnusedUnparser {}),
]);
let sql = unparser.plan_to_sql(&plan)?;
- let expected = "SELECT j1.j1_id AS user_id FROM (SELECT * FROM j1)";
+ let expected =
+ "SELECT j1.j1_id AS user_id FROM (SELECT j1.j1_id, j1.j1_string FROM
j1)";
assert_eq!(sql.to_string(), expected);
if let Some(err) = plan_to_sql(&plan).err() {
diff --git a/datafusion/sql/tests/sql_integration.rs
b/datafusion/sql/tests/sql_integration.rs
index 1df1830268..6877e78264 100644
--- a/datafusion/sql/tests/sql_integration.rs
+++ b/datafusion/sql/tests/sql_integration.rs
@@ -54,15 +54,6 @@ use sqlparser::dialect::{Dialect, GenericDialect,
HiveDialect, MySqlDialect};
mod cases;
mod common;
-#[test]
-fn test_schema_support() {
- quick_test(
- "SELECT * FROM s1.test",
- "Projection: *\
- \n TableScan: s1.test",
- );
-}
-
#[test]
fn parse_decimals() {
let test_data = [
@@ -449,19 +440,6 @@ Explain
quick_test(sql, plan);
}
-#[test]
-fn plan_copy_to_query() {
- let sql = "COPY (select * from test_decimal limit 10) to 'output.csv'";
- let plan = r#"
-CopyTo: format=csv output_url=output.csv options: ()
- Limit: skip=0, fetch=10
- Projection: *
- TableScan: test_decimal
- "#
- .trim();
- quick_test(sql, plan);
-}
-
#[test]
fn plan_insert() {
let sql =
@@ -585,15 +563,6 @@ fn select_repeated_column() {
);
}
-#[test]
-fn select_wildcard_with_repeated_column_but_is_aliased() {
- quick_test(
- "SELECT *, first_name AS fn from person",
- "Projection: *, person.first_name AS fn\
- \n TableScan: person",
- );
-}
-
#[test]
fn select_scalar_func_with_literal_no_relation() {
quick_test(
@@ -793,30 +762,6 @@ fn join_with_ambiguous_column() {
quick_test(sql, expected);
}
-#[test]
-fn where_selection_with_ambiguous_column() {
- let sql = "SELECT * FROM person a, person b WHERE id = id + 1";
- let err = logical_plan(sql)
- .expect_err("query should have failed")
- .strip_backtrace();
- assert_eq!(
- "\"Schema error: Ambiguous reference to unqualified field id\"",
- format!("{err:?}")
- );
-}
-
-#[test]
-fn natural_join() {
- let sql = "SELECT * FROM lineitem a NATURAL JOIN lineitem b";
- let expected = "Projection: *\
- \n Inner Join: Using a.l_item_id = b.l_item_id,
a.l_description = b.l_description, a.price = b.price\
- \n SubqueryAlias: a\
- \n TableScan: lineitem\
- \n SubqueryAlias: b\
- \n TableScan: lineitem";
- quick_test(sql, expected);
-}
-
#[test]
fn natural_left_join() {
let sql = "SELECT l_item_id FROM lineitem a NATURAL LEFT JOIN lineitem b";
@@ -841,83 +786,6 @@ fn natural_right_join() {
quick_test(sql, expected);
}
-#[test]
-fn natural_join_no_common_becomes_cross_join() {
- let sql = "SELECT * FROM person a NATURAL JOIN lineitem b";
- let expected = "Projection: *\
- \n Cross Join: \
- \n SubqueryAlias: a\
- \n TableScan: person\
- \n SubqueryAlias: b\
- \n TableScan: lineitem";
- quick_test(sql, expected);
-}
-
-#[test]
-fn using_join_multiple_keys() {
- let sql = "SELECT * FROM person a join person b using (id, age)";
- let expected = "Projection: *\
- \n Inner Join: Using a.id = b.id, a.age = b.age\
- \n SubqueryAlias: a\
- \n TableScan: person\
- \n SubqueryAlias: b\
- \n TableScan: person";
- quick_test(sql, expected);
-}
-
-#[test]
-fn using_join_multiple_keys_subquery() {
- let sql =
- "SELECT age FROM (SELECT * FROM person a join person b using (id, age,
state))";
- let expected = "Projection: a.age\
- \n Projection: *\
- \n Inner Join: Using a.id = b.id, a.age = b.age,
a.state = b.state\
- \n SubqueryAlias: a\
- \n TableScan: person\
- \n SubqueryAlias: b\
- \n TableScan: person";
- quick_test(sql, expected);
-}
-
-#[test]
-fn using_join_multiple_keys_qualified_wildcard_select() {
- let sql = "SELECT a.* FROM person a join person b using (id, age)";
- let expected = "Projection: a.*\
- \n Inner Join: Using a.id = b.id, a.age = b.age\
- \n SubqueryAlias: a\
- \n TableScan: person\
- \n SubqueryAlias: b\
- \n TableScan: person";
- quick_test(sql, expected);
-}
-
-#[test]
-fn using_join_multiple_keys_select_all_columns() {
- let sql = "SELECT a.*, b.* FROM person a join person b using (id, age)";
- let expected = "Projection: a.*, b.*\
- \n Inner Join: Using a.id = b.id, a.age = b.age\
- \n SubqueryAlias: a\
- \n TableScan: person\
- \n SubqueryAlias: b\
- \n TableScan: person";
- quick_test(sql, expected);
-}
-
-#[test]
-fn using_join_multiple_keys_multiple_joins() {
- let sql = "SELECT * FROM person a join person b using (id, age, state)
join person c using (id, age, state)";
- let expected = "Projection: *\
- \n Inner Join: Using a.id = c.id, a.age = c.age,
a.state = c.state\
- \n Inner Join: Using a.id = b.id, a.age = b.age,
a.state = b.state\
- \n SubqueryAlias: a\
- \n TableScan: person\
- \n SubqueryAlias: b\
- \n TableScan: person\
- \n SubqueryAlias: c\
- \n TableScan: person";
- quick_test(sql, expected);
-}
-
#[test]
fn select_with_having() {
let sql = "SELECT id, age
@@ -1233,24 +1101,6 @@ fn select_binary_expr_nested() {
quick_test(sql, expected);
}
-#[test]
-fn select_wildcard_with_groupby() {
- quick_test(
- r#"SELECT * FROM person GROUP BY id, first_name, last_name, age,
state, salary, birth_date, "😀""#,
- "Projection: *\
- \n Aggregate: groupBy=[[person.id, person.first_name,
person.last_name, person.age, person.state, person.salary, person.birth_date,
person.😀]], aggr=[[]]\
- \n TableScan: person",
- );
- quick_test(
- "SELECT * FROM (SELECT first_name, last_name FROM person) AS a
GROUP BY first_name, last_name",
- "Projection: *\
- \n Aggregate: groupBy=[[a.first_name, a.last_name]], aggr=[[]]\
- \n SubqueryAlias: a\
- \n Projection: person.first_name, person.last_name\
- \n TableScan: person",
- );
-}
-
#[test]
fn select_simple_aggregate() {
quick_test(
@@ -1397,56 +1247,6 @@ fn select_interval_out_of_range() {
);
}
-#[test]
-fn recursive_ctes() {
- let sql = "
- WITH RECURSIVE numbers AS (
- select 1 as n
- UNION ALL
- select n + 1 FROM numbers WHERE N < 10
- )
- select * from numbers;";
- quick_test(
- sql,
- "Projection: *\
- \n SubqueryAlias: numbers\
- \n RecursiveQuery: is_distinct=false\
- \n Projection: Int64(1) AS n\
- \n EmptyRelation\
- \n Projection: numbers.n + Int64(1)\
- \n Filter: numbers.n < Int64(10)\
- \n TableScan: numbers",
- )
-}
-
-#[test]
-fn recursive_ctes_disabled() {
- let sql = "
- WITH RECURSIVE numbers AS (
- select 1 as n
- UNION ALL
- select n + 1 FROM numbers WHERE N < 10
- )
- select * from numbers;";
-
- // manually setting up test here so that we can disable recursive ctes
- let mut state = MockSessionState::default();
- state.config_options.execution.enable_recursive_ctes = false;
- let context = MockContextProvider { state };
-
- let planner = SqlToRel::new_with_options(&context,
ParserOptions::default());
- let result = DFParser::parse_sql_with_dialect(sql, &GenericDialect {});
- let mut ast = result.unwrap();
-
- let err = planner
- .statement_to_plan(ast.pop_front().unwrap())
- .expect_err("query should have failed");
- assert_eq!(
- "This feature is not implemented: Recursive CTEs are not enabled",
- err.strip_backtrace()
- );
-}
-
#[test]
fn
select_simple_aggregate_with_groupby_and_column_is_in_aggregate_and_groupby() {
quick_test(
@@ -1618,15 +1418,6 @@ fn
select_aggregate_with_non_column_inner_expression_with_groupby() {
);
}
-#[test]
-fn test_wildcard() {
- quick_test(
- "SELECT * from person",
- "Projection: *\
- \n TableScan: person",
- );
-}
-
#[test]
fn select_count_one() {
let sql = "SELECT count(1) FROM person";
@@ -2060,20 +1851,6 @@ fn join_with_using() {
quick_test(sql, expected);
}
-#[test]
-fn project_wildcard_on_join_with_using() {
- let sql = "SELECT * \
- FROM lineitem \
- JOIN lineitem as lineitem2 \
- USING (l_item_id)";
- let expected = "Projection: *\
- \n Inner Join: Using lineitem.l_item_id = lineitem2.l_item_id\
- \n TableScan: lineitem\
- \n SubqueryAlias: lineitem2\
- \n TableScan: lineitem";
- quick_test(sql, expected);
-}
-
#[test]
fn equijoin_explicit_syntax_3_tables() {
let sql = "SELECT id, order_id, l_description \
@@ -2867,24 +2644,6 @@ fn exists_subquery_schema_outer_schema_overlap() {
quick_test(sql, expected);
}
-#[test]
-fn exists_subquery_wildcard() {
- let sql = "SELECT id FROM person p WHERE EXISTS \
- (SELECT * FROM person \
- WHERE last_name = p.last_name \
- AND state = p.state)";
-
- let expected = "Projection: p.id\
- \n Filter: EXISTS (<subquery>)\
- \n Subquery:\
- \n Projection: *\
- \n Filter: person.last_name = outer_ref(p.last_name) AND
person.state = outer_ref(p.state)\
- \n TableScan: person\
- \n SubqueryAlias: p\
- \n TableScan: person";
- quick_test(sql, expected);
-}
-
#[test]
fn in_subquery_uncorrelated() {
let sql = "SELECT id FROM person p WHERE id IN \
@@ -2958,88 +2717,6 @@ fn scalar_subquery_reference_outer_field() {
quick_test(sql, expected);
}
-#[test]
-fn subquery_references_cte() {
- let sql = "WITH \
- cte AS (SELECT * FROM person) \
- SELECT * FROM person WHERE EXISTS (SELECT * FROM cte WHERE id =
person.id)";
-
- let expected = "Projection: *\
- \n Filter: EXISTS (<subquery>)\
- \n Subquery:\
- \n Projection: *\
- \n Filter: cte.id = outer_ref(person.id)\
- \n SubqueryAlias: cte\
- \n Projection: *\
- \n TableScan: person\
- \n TableScan: person";
-
- quick_test(sql, expected)
-}
-
-#[test]
-fn cte_with_no_column_names() {
- let sql = "WITH \
- numbers AS ( \
- SELECT 1 as a, 2 as b, 3 as c \
- ) \
- SELECT * FROM numbers;";
-
- let expected = "Projection: *\
- \n SubqueryAlias: numbers\
- \n Projection: Int64(1) AS a, Int64(2) AS b, Int64(3) AS c\
- \n EmptyRelation";
-
- quick_test(sql, expected)
-}
-
-#[test]
-fn cte_with_column_names() {
- let sql = "WITH \
- numbers(a, b, c) AS ( \
- SELECT 1, 2, 3 \
- ) \
- SELECT * FROM numbers;";
-
- let expected = "Projection: *\
- \n SubqueryAlias: numbers\
- \n Projection: Int64(1) AS a, Int64(2) AS b, Int64(3) AS c\
- \n Projection: Int64(1), Int64(2), Int64(3)\
- \n EmptyRelation";
-
- quick_test(sql, expected)
-}
-
-#[test]
-fn cte_with_column_aliases_precedence() {
- // The end result should always be what CTE specification says
- let sql = "WITH \
- numbers(a, b, c) AS ( \
- SELECT 1 as x, 2 as y, 3 as z \
- ) \
- SELECT * FROM numbers;";
-
- let expected = "Projection: *\
- \n SubqueryAlias: numbers\
- \n Projection: x AS a, y AS b, z AS c\
- \n Projection: Int64(1) AS x, Int64(2) AS y, Int64(3) AS z\
- \n EmptyRelation";
- quick_test(sql, expected)
-}
-
-#[test]
-fn cte_unbalanced_number_of_columns() {
- let sql = "WITH \
- numbers(a) AS ( \
- SELECT 1, 2, 3 \
- ) \
- SELECT * FROM numbers;";
-
- let expected = "Error during planning: Source table contains 3 columns but
only 1 names given as column alias";
- let result = logical_plan(sql).err().unwrap();
- assert_eq!(result.strip_backtrace(), expected);
-}
-
#[test]
fn aggregate_with_rollup() {
let sql =
@@ -3133,128 +2810,6 @@ fn join_on_complex_condition() {
quick_test(sql, expected);
}
-#[test]
-fn lateral_constant() {
- let sql = "SELECT * FROM j1, LATERAL (SELECT 1) AS j2";
- let expected = "Projection: *\
- \n Cross Join: \
- \n TableScan: j1\
- \n SubqueryAlias: j2\
- \n Projection: Int64(1)\
- \n EmptyRelation";
- quick_test(sql, expected);
-}
-
-#[test]
-fn lateral_comma_join() {
- let sql = "SELECT j1_string, j2_string FROM
- j1, \
- LATERAL (SELECT * FROM j2 WHERE j1_id < j2_id) AS j2";
- let expected = "Projection: j1.j1_string, j2.j2_string\
- \n Cross Join: \
- \n TableScan: j1\
- \n SubqueryAlias: j2\
- \n Subquery:\
- \n Projection: *\
- \n Filter: outer_ref(j1.j1_id) < j2.j2_id\
- \n TableScan: j2";
- quick_test(sql, expected);
-}
-
-#[test]
-fn lateral_comma_join_referencing_join_rhs() {
- let sql = "SELECT * FROM\
- \n j1 JOIN (j2 JOIN j3 ON(j2_id = j3_id - 2)) ON(j1_id = j2_id),\
- \n LATERAL (SELECT * FROM j3 WHERE j3_string = j2_string) as j4;";
- let expected = "Projection: *\
- \n Cross Join: \
- \n Inner Join: Filter: j1.j1_id = j2.j2_id\
- \n TableScan: j1\
- \n Inner Join: Filter: j2.j2_id = j3.j3_id - Int64(2)\
- \n TableScan: j2\
- \n TableScan: j3\
- \n SubqueryAlias: j4\
- \n Subquery:\
- \n Projection: *\
- \n Filter: j3.j3_string = outer_ref(j2.j2_string)\
- \n TableScan: j3";
- quick_test(sql, expected);
-}
-
-#[test]
-fn lateral_comma_join_with_shadowing() {
- // The j1_id on line 3 references the (closest) j1 definition from line 2.
- let sql = "\
- SELECT * FROM j1, LATERAL (\
- SELECT * FROM j1, LATERAL (\
- SELECT * FROM j2 WHERE j1_id = j2_id\
- ) as j2\
- ) as j2;";
- let expected = "Projection: *\
- \n Cross Join: \
- \n TableScan: j1\
- \n SubqueryAlias: j2\
- \n Subquery:\
- \n Projection: *\
- \n Cross Join: \
- \n TableScan: j1\
- \n SubqueryAlias: j2\
- \n Subquery:\
- \n Projection: *\
- \n Filter: outer_ref(j1.j1_id) = j2.j2_id\
- \n TableScan: j2";
- quick_test(sql, expected);
-}
-
-#[test]
-fn lateral_left_join() {
- let sql = "SELECT j1_string, j2_string FROM \
- j1 \
- LEFT JOIN LATERAL (SELECT * FROM j2 WHERE j1_id < j2_id) AS j2
ON(true);";
- let expected = "Projection: j1.j1_string, j2.j2_string\
- \n Left Join: Filter: Boolean(true)\
- \n TableScan: j1\
- \n SubqueryAlias: j2\
- \n Subquery:\
- \n Projection: *\
- \n Filter: outer_ref(j1.j1_id) < j2.j2_id\
- \n TableScan: j2";
- quick_test(sql, expected);
-}
-
-#[test]
-fn lateral_nested_left_join() {
- let sql = "SELECT * FROM
- j1, \
- (j2 LEFT JOIN LATERAL (SELECT * FROM j3 WHERE j1_id + j2_id =
j3_id) AS j3 ON(true))";
- let expected = "Projection: *\
- \n Cross Join: \
- \n TableScan: j1\
- \n Left Join: Filter: Boolean(true)\
- \n TableScan: j2\
- \n SubqueryAlias: j3\
- \n Subquery:\
- \n Projection: *\
- \n Filter: outer_ref(j1.j1_id) + outer_ref(j2.j2_id) =
j3.j3_id\
- \n TableScan: j3";
- quick_test(sql, expected);
-}
-
-#[test]
-fn lateral_unnest() {
- let sql = "SELECT * from unnest_table u, unnest(u.array_col)";
- let expected = "Projection: *\
- \n Cross Join: \
- \n SubqueryAlias: u\
- \n TableScan: unnest_table\
- \n Subquery:\
- \n Projection:
__unnest_placeholder(outer_ref(u.array_col),depth=1) AS
UNNEST(outer_ref(u.array_col))\
- \n Unnest:
lists[__unnest_placeholder(outer_ref(u.array_col))|depth=1] structs[]\
- \n Projection: outer_ref(u.array_col) AS
__unnest_placeholder(outer_ref(u.array_col))\
- \n EmptyRelation";
- quick_test(sql, expected);
-}
-
#[test]
fn hive_aggregate_with_filter() -> Result<()> {
let dialect = &HiveDialect {};
@@ -3515,20 +3070,6 @@ fn test_one_side_constant_full_join() {
quick_test(sql, expected);
}
-#[test]
-fn test_select_all_inner_join() {
- let sql = "SELECT *
- FROM person \
- INNER JOIN orders \
- ON orders.customer_id * 2 = person.id + 10";
-
- let expected = "Projection: *\
- \n Inner Join: Filter: orders.customer_id * Int64(2) = person.id
+ Int64(10)\
- \n TableScan: person\
- \n TableScan: orders";
- quick_test(sql, expected);
-}
-
#[test]
fn test_select_join_key_inner_join() {
let sql = "SELECT orders.customer_id * 2, person.id + 10
@@ -4258,34 +3799,6 @@ fn test_prepare_statement_to_plan_limit() {
prepare_stmt_replace_params_quick_test(plan, param_values, expected_plan);
}
-#[test]
-fn test_prepare_statement_to_plan_value_list() {
- let sql = "PREPARE my_plan(STRING, STRING) AS SELECT * FROM (VALUES(1,
$1), (2, $2)) AS t (num, letter);";
-
- let expected_plan = "Prepare: \"my_plan\" [Utf8, Utf8] \
- \n Projection: *\
- \n SubqueryAlias: t\
- \n Projection: column1 AS num, column2 AS letter\
- \n Values: (Int64(1), $1), (Int64(2), $2)";
-
- let expected_dt = "[Utf8, Utf8]";
-
- let plan = prepare_stmt_quick_test(sql, expected_plan, expected_dt);
-
- ///////////////////
- // replace params with values
- let param_values = vec![
- ScalarValue::from("a".to_string()),
- ScalarValue::from("b".to_string()),
- ];
- let expected_plan = "Projection: *\
- \n SubqueryAlias: t\
- \n Projection: column1 AS num, column2 AS letter\
- \n Values: (Int64(1), Utf8(\"a\") AS $1), (Int64(2), Utf8(\"b\")
AS $2)";
-
- prepare_stmt_replace_params_quick_test(plan, param_values, expected_plan);
-}
-
#[test]
fn test_prepare_statement_unknown_list_param() {
let sql = "SELECT id from person where id = $2";
@@ -4320,44 +3833,6 @@ fn test_prepare_statement_bad_list_idx() {
assert_contains!(err.to_string(), "Error during planning: Failed to parse
placeholder id: invalid digit found in string");
}
-#[test]
-fn test_table_alias() {
- let sql = "select * from (\
- (select id from person) t1 \
- CROSS JOIN \
- (select age from person) t2 \
- ) as f";
-
- let expected = "Projection: *\
- \n SubqueryAlias: f\
- \n Cross Join: \
- \n SubqueryAlias: t1\
- \n Projection: person.id\
- \n TableScan: person\
- \n SubqueryAlias: t2\
- \n Projection: person.age\
- \n TableScan: person";
- quick_test(sql, expected);
-
- let sql = "select * from (\
- (select id from person) t1 \
- CROSS JOIN \
- (select age from person) t2 \
- ) as f (c1, c2)";
-
- let expected = "Projection: *\
- \n SubqueryAlias: f\
- \n Projection: t1.id AS c1, t2.age AS c2\
- \n Cross Join: \
- \n SubqueryAlias: t1\
- \n Projection: person.id\
- \n TableScan: person\
- \n SubqueryAlias: t2\
- \n Projection: person.age\
- \n TableScan: person";
- quick_test(sql, expected);
-}
-
#[test]
fn test_inner_join_with_cast_key() {
let sql = "SELECT person.id, person.age
diff --git a/datafusion/sqllogictest/test_files/alias.slt
b/datafusion/sqllogictest/test_files/alias.slt
new file mode 100644
index 0000000000..340ffb6078
--- /dev/null
+++ b/datafusion/sqllogictest/test_files/alias.slt
@@ -0,0 +1,59 @@
+
+# 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.
+
+
+# test table alias
+statement count 0
+create table t1(id int);
+
+statement count 0
+create table t2(age int);
+
+query TT
+explain select * from ((select id from t1) cross join (select age from t2)) as
f;
+----
+logical_plan
+01)SubqueryAlias: f
+02)--Cross Join:
+03)----TableScan: t1 projection=[id]
+04)----TableScan: t2 projection=[age]
+physical_plan
+01)CrossJoinExec
+02)--DataSourceExec: partitions=1, partition_sizes=[0]
+03)--DataSourceExec: partitions=1, partition_sizes=[0]
+
+query TT
+explain select * from ((select id from t1) cross join (select age from t2)) as
f(c1, c2);
+----
+logical_plan
+01)SubqueryAlias: f
+02)--Projection: t1.id AS c1, t2.age AS c2
+03)----Cross Join:
+04)------TableScan: t1 projection=[id]
+05)------TableScan: t2 projection=[age]
+physical_plan
+01)ProjectionExec: expr=[id@0 as c1, age@1 as c2]
+02)--CrossJoinExec
+03)----DataSourceExec: partitions=1, partition_sizes=[0]
+04)----DataSourceExec: partitions=1, partition_sizes=[0]
+
+statement count 0
+drop table t1;
+
+statement count 0
+drop table t2;
\ No newline at end of file
diff --git a/datafusion/sqllogictest/test_files/copy.slt
b/datafusion/sqllogictest/test_files/copy.slt
index f39ff56ce4..e2bb23e357 100644
--- a/datafusion/sqllogictest/test_files/copy.slt
+++ b/datafusion/sqllogictest/test_files/copy.slt
@@ -631,3 +631,20 @@ COPY source_table to '/tmp/table.parquet' (row_group_size
55 + 102);
# Copy using execution.keep_partition_by_columns with an invalid value
query error DataFusion error: Invalid or Unsupported Configuration: provided
value for 'execution.keep_partition_by_columns' was not recognized:
"invalid_value"
COPY source_table to '/tmp/table.parquet' OPTIONS
(execution.keep_partition_by_columns invalid_value);
+
+statement count 0
+create table t;
+
+query TT
+explain COPY (select * from t limit 10) to 'output.csv';
+----
+logical_plan
+01)CopyTo: format=csv output_url=output.csv options: ()
+02)--Limit: skip=0, fetch=10
+03)----TableScan: t projection=[], fetch=10
+physical_plan
+01)DataSinkExec: sink=CsvSink(file_groups=[])
+02)--DataSourceExec: partitions=1, partition_sizes=[0], fetch=10
+
+statement count 0
+drop table t;
diff --git a/datafusion/sqllogictest/test_files/cte.slt
b/datafusion/sqllogictest/test_files/cte.slt
index 95b9b5a925..e019af9775 100644
--- a/datafusion/sqllogictest/test_files/cte.slt
+++ b/datafusion/sqllogictest/test_files/cte.slt
@@ -859,3 +859,149 @@ SELECT * FROM
400 500 1
400 500 2
400 500 3
+
+query error DataFusion error: Error during planning: Source table contains 3
columns but only 1 names given as column alias
+with numbers(a) as (select 1, 2, 3) select * from numbers;
+
+query TT
+explain with numbers(a,b,c) as (select 1 as x, 2 as y, 3 as z) select * from
numbers;
+----
+logical_plan
+01)SubqueryAlias: numbers
+02)--Projection: Int64(1) AS a, Int64(2) AS b, Int64(3) AS c
+03)----EmptyRelation
+physical_plan
+01)ProjectionExec: expr=[1 as a, 2 as b, 3 as c]
+02)--PlaceholderRowExec
+
+query TT
+explain with numbers(a,b,c) as (select 1,2,3) select * from numbers;
+----
+logical_plan
+01)SubqueryAlias: numbers
+02)--Projection: Int64(1) AS a, Int64(2) AS b, Int64(3) AS c
+03)----EmptyRelation
+physical_plan
+01)ProjectionExec: expr=[1 as a, 2 as b, 3 as c]
+02)--PlaceholderRowExec
+
+query TT
+explain with numbers as (select 1 as a, 2 as b, 3 as c) select * from numbers;
+----
+logical_plan
+01)SubqueryAlias: numbers
+02)--Projection: Int64(1) AS a, Int64(2) AS b, Int64(3) AS c
+03)----EmptyRelation
+physical_plan
+01)ProjectionExec: expr=[1 as a, 2 as b, 3 as c]
+02)--PlaceholderRowExec
+
+statement count 0
+create table person (id int, name string, primary key(id))
+
+query TT
+explain with cte as (select * from person) SELECT * FROM person WHERE EXISTS
(SELECT * FROM cte WHERE id = person.id);
+----
+logical_plan
+01)LeftSemi Join: person.id = __correlated_sq_1.id
+02)--TableScan: person projection=[id, name]
+03)--SubqueryAlias: __correlated_sq_1
+04)----SubqueryAlias: cte
+05)------TableScan: person projection=[id]
+physical_plan
+01)CoalesceBatchesExec: target_batch_size=8182
+02)--HashJoinExec: mode=Partitioned, join_type=LeftSemi, on=[(id@0, id@0)]
+03)----DataSourceExec: partitions=1, partition_sizes=[0]
+04)----DataSourceExec: partitions=1, partition_sizes=[0]
+
+statement count 0
+drop table person;
+
+statement count 0
+create table j1(a int);
+
+statement count 0
+create table j2(b int);
+
+query TT
+explain SELECT * FROM j1, LATERAL (SELECT 1) AS j2;
+----
+logical_plan
+01)Cross Join:
+02)--TableScan: j1 projection=[a]
+03)--SubqueryAlias: j2
+04)----Projection: Int64(1)
+05)------EmptyRelation
+physical_plan
+01)CrossJoinExec
+02)--DataSourceExec: partitions=1, partition_sizes=[0]
+03)--ProjectionExec: expr=[1 as Int64(1)]
+04)----PlaceholderRowExec
+
+statement count 0
+drop table j1;
+
+statement count 0
+drop table j2;
+
+query TT
+explain WITH RECURSIVE numbers AS (
+ select 1 as n
+ UNION ALL
+ select n + 1 FROM numbers WHERE N < 10
+) select * from numbers;
+----
+logical_plan
+01)SubqueryAlias: numbers
+02)--RecursiveQuery: is_distinct=false
+03)----Projection: Int64(1) AS n
+04)------EmptyRelation
+05)----Projection: numbers.n + Int64(1)
+06)------Filter: numbers.n < Int64(10)
+07)--------TableScan: numbers
+physical_plan
+01)RecursiveQueryExec: name=numbers, is_distinct=false
+02)--ProjectionExec: expr=[1 as n]
+03)----PlaceholderRowExec
+04)--CoalescePartitionsExec
+05)----ProjectionExec: expr=[n@0 + 1 as numbers.n + Int64(1)]
+06)------CoalesceBatchesExec: target_batch_size=8182
+07)--------FilterExec: n@0 < 10
+08)----------RepartitionExec: partitioning=RoundRobinBatch(4),
input_partitions=1
+09)------------WorkTableExec: name=numbers
+
+query TT
+explain WITH RECURSIVE numbers AS (
+ select 1 as n
+ UNION ALL
+ select n + 1 FROM numbers WHERE N < 10
+) select * from numbers;
+----
+logical_plan
+01)SubqueryAlias: numbers
+02)--RecursiveQuery: is_distinct=false
+03)----Projection: Int64(1) AS n
+04)------EmptyRelation
+05)----Projection: numbers.n + Int64(1)
+06)------Filter: numbers.n < Int64(10)
+07)--------TableScan: numbers
+physical_plan
+01)RecursiveQueryExec: name=numbers, is_distinct=false
+02)--ProjectionExec: expr=[1 as n]
+03)----PlaceholderRowExec
+04)--CoalescePartitionsExec
+05)----ProjectionExec: expr=[n@0 + 1 as numbers.n + Int64(1)]
+06)------CoalesceBatchesExec: target_batch_size=8182
+07)--------FilterExec: n@0 < 10
+08)----------RepartitionExec: partitioning=RoundRobinBatch(4),
input_partitions=1
+09)------------WorkTableExec: name=numbers
+
+statement count 0
+set datafusion.execution.enable_recursive_ctes = false;
+
+query error DataFusion error: This feature is not implemented: Recursive CTEs
are not enabled
+explain WITH RECURSIVE numbers AS (
+ select 1 as n
+ UNION ALL
+ select n + 1 FROM numbers WHERE N < 10
+) select * from numbers;
diff --git a/datafusion/sqllogictest/test_files/group_by.slt
b/datafusion/sqllogictest/test_files/group_by.slt
index 2b3ebcda15..0cc8045dcc 100644
--- a/datafusion/sqllogictest/test_files/group_by.slt
+++ b/datafusion/sqllogictest/test_files/group_by.slt
@@ -5537,3 +5537,32 @@ drop view t
statement ok
drop table source;
+
+
+# test select_wildcard_with_groupby
+statement count 0
+create table t(a int, b int, c int, "😀" int);
+
+query TT
+explain select * from t group by a, b, c, "😀";
+----
+logical_plan
+01)Aggregate: groupBy=[[t.a, t.b, t.c, t.😀]], aggr=[[]]
+02)--TableScan: t projection=[a, b, c, 😀]
+physical_plan
+01)AggregateExec: mode=Single, gby=[a@0 as a, b@1 as b, c@2 as c, 😀@3 as 😀],
aggr=[]
+02)--DataSourceExec: partitions=1, partition_sizes=[0]
+
+query TT
+explain select * from (select a, b from t) as c group by a, b;
+----
+logical_plan
+01)Aggregate: groupBy=[[c.a, c.b]], aggr=[[]]
+02)--SubqueryAlias: c
+03)----TableScan: t projection=[a, b]
+physical_plan
+01)AggregateExec: mode=Single, gby=[a@0 as a, b@1 as b], aggr=[]
+02)--DataSourceExec: partitions=1, partition_sizes=[0]
+
+statement count 0
+drop table t;
diff --git a/datafusion/sqllogictest/test_files/joins.slt
b/datafusion/sqllogictest/test_files/joins.slt
index 0397e0c367..50af06dc40 100644
--- a/datafusion/sqllogictest/test_files/joins.slt
+++ b/datafusion/sqllogictest/test_files/joins.slt
@@ -4541,3 +4541,269 @@ DROP TABLE test
statement ok
set datafusion.execution.target_partitions = 1;
+
+# test using_join_multiple_keys_subquery
+statement count 0
+create table person(id int, age int, state int);
+
+statement count 0
+create table lineitem(c1 int);
+
+query TT
+explain SELECT * FROM person a join person b using (id, age);
+----
+logical_plan
+01)Projection: a.id, a.age, a.state, b.state
+02)--Inner Join: a.id = b.id, a.age = b.age
+03)----SubqueryAlias: a
+04)------TableScan: person projection=[id, age, state]
+05)----SubqueryAlias: b
+06)------TableScan: person projection=[id, age, state]
+physical_plan
+01)CoalesceBatchesExec: target_batch_size=3
+02)--HashJoinExec: mode=CollectLeft, join_type=Inner, on=[(id@0, id@0),
(age@1, age@1)], projection=[id@0, age@1, state@2, state@5]
+03)----DataSourceExec: partitions=1, partition_sizes=[0]
+04)----DataSourceExec: partitions=1, partition_sizes=[0]
+
+query TT
+explain SELECT age FROM (SELECT * FROM person a join person b using (id, age,
state));
+----
+logical_plan
+01)Projection: a.age
+02)--Inner Join: a.id = b.id, a.age = b.age, a.state = b.state
+03)----SubqueryAlias: a
+04)------TableScan: person projection=[id, age, state]
+05)----SubqueryAlias: b
+06)------TableScan: person projection=[id, age, state]
+physical_plan
+01)CoalesceBatchesExec: target_batch_size=3
+02)--HashJoinExec: mode=CollectLeft, join_type=Inner, on=[(id@0, id@0),
(age@1, age@1), (state@2, state@2)], projection=[age@1]
+03)----DataSourceExec: partitions=1, partition_sizes=[0]
+04)----DataSourceExec: partitions=1, partition_sizes=[0]
+
+query TT
+explain SELECT a.* FROM person a join person b using (id, age);
+----
+logical_plan
+01)Projection: a.id, a.age, a.state
+02)--Inner Join: a.id = b.id, a.age = b.age
+03)----SubqueryAlias: a
+04)------TableScan: person projection=[id, age, state]
+05)----SubqueryAlias: b
+06)------TableScan: person projection=[id, age]
+physical_plan
+01)CoalesceBatchesExec: target_batch_size=3
+02)--HashJoinExec: mode=CollectLeft, join_type=Inner, on=[(id@0, id@0),
(age@1, age@1)], projection=[id@0, age@1, state@2]
+03)----DataSourceExec: partitions=1, partition_sizes=[0]
+04)----DataSourceExec: partitions=1, partition_sizes=[0]
+
+query TT
+explain SELECT a.*, b.* FROM person a join person b using (id, age);
+----
+logical_plan
+01)Inner Join: a.id = b.id, a.age = b.age
+02)--SubqueryAlias: a
+03)----TableScan: person projection=[id, age, state]
+04)--SubqueryAlias: b
+05)----TableScan: person projection=[id, age, state]
+physical_plan
+01)CoalesceBatchesExec: target_batch_size=3
+02)--HashJoinExec: mode=CollectLeft, join_type=Inner, on=[(id@0, id@0),
(age@1, age@1)]
+03)----DataSourceExec: partitions=1, partition_sizes=[0]
+04)----DataSourceExec: partitions=1, partition_sizes=[0]
+
+query TT
+explain SELECT * FROM person a join person b using (id, age, state) join
person c using (id, age, state);
+----
+logical_plan
+01)Projection: a.id, a.age, a.state
+02)--Inner Join: a.id = c.id, a.age = c.age, a.state = c.state
+03)----Projection: a.id, a.age, a.state
+04)------Inner Join: a.id = b.id, a.age = b.age, a.state = b.state
+05)--------SubqueryAlias: a
+06)----------TableScan: person projection=[id, age, state]
+07)--------SubqueryAlias: b
+08)----------TableScan: person projection=[id, age, state]
+09)----SubqueryAlias: c
+10)------TableScan: person projection=[id, age, state]
+physical_plan
+01)CoalesceBatchesExec: target_batch_size=3
+02)--HashJoinExec: mode=CollectLeft, join_type=Inner, on=[(id@0, id@0),
(age@1, age@1), (state@2, state@2)], projection=[id@0, age@1, state@2]
+03)----CoalesceBatchesExec: target_batch_size=3
+04)------HashJoinExec: mode=CollectLeft, join_type=Inner, on=[(id@0, id@0),
(age@1, age@1), (state@2, state@2)], projection=[id@0, age@1, state@2]
+05)--------DataSourceExec: partitions=1, partition_sizes=[0]
+06)--------DataSourceExec: partitions=1, partition_sizes=[0]
+07)----DataSourceExec: partitions=1, partition_sizes=[0]
+
+query TT
+explain SELECT * FROM person a NATURAL JOIN lineitem b;
+----
+logical_plan
+01)Cross Join:
+02)--SubqueryAlias: a
+03)----TableScan: person projection=[id, age, state]
+04)--SubqueryAlias: b
+05)----TableScan: lineitem projection=[c1]
+physical_plan
+01)CrossJoinExec
+02)--DataSourceExec: partitions=1, partition_sizes=[0]
+03)--DataSourceExec: partitions=1, partition_sizes=[0]
+
+query TT
+explain SELECT * FROM lineitem JOIN lineitem as lineitem2 USING (c1)
+----
+logical_plan
+01)Projection: lineitem.c1
+02)--Inner Join: lineitem.c1 = lineitem2.c1
+03)----TableScan: lineitem projection=[c1]
+04)----SubqueryAlias: lineitem2
+05)------TableScan: lineitem projection=[c1]
+physical_plan
+01)CoalesceBatchesExec: target_batch_size=3
+02)--HashJoinExec: mode=CollectLeft, join_type=Inner, on=[(c1@0, c1@0)],
projection=[c1@0]
+03)----DataSourceExec: partitions=1, partition_sizes=[0]
+04)----DataSourceExec: partitions=1, partition_sizes=[0]
+
+statement count 0
+drop table person;
+
+statement count 0
+drop table lineitem;
+
+statement count 0
+create table j1(j1_string varchar, j1_id int);
+
+statement count 0
+create table j2(j2_string varchar, j2_id int);
+
+statement count 0
+create table j3(j3_string varchar, j3_id int);
+
+statement count 0
+create table j4(j4_string varchar, j4_id int);
+
+query TT
+explain SELECT j1_string, j2_string FROM j1, LATERAL (SELECT * FROM j2 WHERE
j1_id < j2_id) AS j2;
+----
+logical_plan
+01)Cross Join:
+02)--TableScan: j1 projection=[j1_string]
+03)--SubqueryAlias: j2
+04)----Projection: j2.j2_string
+05)------Subquery:
+06)--------Filter: outer_ref(j1.j1_id) < j2.j2_id
+07)----------TableScan: j2 projection=[j2_string, j2_id]
+physical_plan_error This feature is not implemented: Physical plan does not
support logical expression OuterReferenceColumn(Int32, Column { relation:
Some(Bare { table: "j1" }), name: "j1_id" })
+
+query TT
+explain SELECT * FROM j1 JOIN (j2 JOIN j3 ON(j2_id = j3_id - 2)) ON(j1_id =
j2_id), LATERAL (SELECT * FROM j3 WHERE j3_string = j2_string) as j4
+----
+logical_plan
+01)Cross Join:
+02)--Inner Join: CAST(j2.j2_id AS Int64) = CAST(j3.j3_id AS Int64) - Int64(2)
+03)----Inner Join: j1.j1_id = j2.j2_id
+04)------TableScan: j1 projection=[j1_string, j1_id]
+05)------TableScan: j2 projection=[j2_string, j2_id]
+06)----TableScan: j3 projection=[j3_string, j3_id]
+07)--SubqueryAlias: j4
+08)----Subquery:
+09)------Filter: j3.j3_string = outer_ref(j2.j2_string)
+10)--------TableScan: j3 projection=[j3_string, j3_id]
+physical_plan_error This feature is not implemented: Physical plan does not
support logical expression OuterReferenceColumn(Utf8, Column { relation:
Some(Bare { table: "j2" }), name: "j2_string" })
+
+query TT
+explain SELECT * FROM j1, LATERAL (SELECT * FROM j1, LATERAL (SELECT * FROM j2
WHERE j1_id = j2_id) as j2) as j2;
+----
+logical_plan
+01)Cross Join:
+02)--TableScan: j1 projection=[j1_string, j1_id]
+03)--SubqueryAlias: j2
+04)----Subquery:
+05)------Cross Join:
+06)--------TableScan: j1 projection=[j1_string, j1_id]
+07)--------SubqueryAlias: j2
+08)----------Subquery:
+09)------------Filter: outer_ref(j1.j1_id) = j2.j2_id
+10)--------------TableScan: j2 projection=[j2_string, j2_id]
+physical_plan_error This feature is not implemented: Physical plan does not
support logical expression OuterReferenceColumn(Int32, Column { relation:
Some(Bare { table: "j1" }), name: "j1_id" })
+
+query TT
+explain SELECT j1_string, j2_string FROM j1 LEFT JOIN LATERAL (SELECT * FROM
j2 WHERE j1_id < j2_id) AS j2 ON(true);
+----
+logical_plan
+01)Left Join:
+02)--TableScan: j1 projection=[j1_string]
+03)--SubqueryAlias: j2
+04)----Projection: j2.j2_string
+05)------Subquery:
+06)--------Filter: outer_ref(j1.j1_id) < j2.j2_id
+07)----------TableScan: j2 projection=[j2_string, j2_id]
+physical_plan_error This feature is not implemented: Physical plan does not
support logical expression OuterReferenceColumn(Int32, Column { relation:
Some(Bare { table: "j1" }), name: "j1_id" })
+
+query TT
+explain SELECT * FROM j1, (j2 LEFT JOIN LATERAL (SELECT * FROM j3 WHERE j1_id
+ j2_id = j3_id) AS j3 ON(true));
+----
+logical_plan
+01)Cross Join:
+02)--TableScan: j1 projection=[j1_string, j1_id]
+03)--Left Join:
+04)----TableScan: j2 projection=[j2_string, j2_id]
+05)----SubqueryAlias: j3
+06)------Subquery:
+07)--------Filter: outer_ref(j1.j1_id) + outer_ref(j2.j2_id) = j3.j3_id
+08)----------TableScan: j3 projection=[j3_string, j3_id]
+physical_plan_error This feature is not implemented: Physical plan does not
support logical expression OuterReferenceColumn(Int32, Column { relation:
Some(Bare { table: "j1" }), name: "j1_id" })
+
+query TT
+explain SELECT * FROM j1, LATERAL (SELECT 1) AS j2;
+----
+logical_plan
+01)Cross Join:
+02)--TableScan: j1 projection=[j1_string, j1_id]
+03)--SubqueryAlias: j2
+04)----Projection: Int64(1)
+05)------EmptyRelation
+physical_plan
+01)CrossJoinExec
+02)--DataSourceExec: partitions=1, partition_sizes=[0]
+03)--ProjectionExec: expr=[1 as Int64(1)]
+04)----PlaceholderRowExec
+
+statement count 0
+drop table j1;
+
+statement count 0
+drop table j2;
+
+statement count 0
+drop table j3;
+
+statement count 0
+drop table j4;
+
+statement count 0
+create table person(id int);
+
+statement count 0
+create table orders(customer_id int);
+
+query TT
+explain SELECT * FROM person INNER JOIN orders ON orders.customer_id * 2 =
person.id + 10
+----
+logical_plan
+01)Inner Join: CAST(person.id AS Int64) + Int64(10) = CAST(orders.customer_id
AS Int64) * Int64(2)
+02)--TableScan: person projection=[id]
+03)--TableScan: orders projection=[customer_id]
+physical_plan
+01)CoalesceBatchesExec: target_batch_size=3
+02)--HashJoinExec: mode=CollectLeft, join_type=Inner, on=[(person.id +
Int64(10)@1, orders.customer_id * Int64(2)@1)], projection=[id@0, customer_id@2]
+03)----ProjectionExec: expr=[id@0 as id, CAST(id@0 AS Int64) + 10 as person.id
+ Int64(10)]
+04)------DataSourceExec: partitions=1, partition_sizes=[0]
+05)----ProjectionExec: expr=[customer_id@0 as customer_id, CAST(customer_id@0
AS Int64) * 2 as orders.customer_id * Int64(2)]
+06)------DataSourceExec: partitions=1, partition_sizes=[0]
+
+statement count 0
+drop table person;
+
+statement count 0
+drop table orders;
diff --git a/datafusion/sqllogictest/test_files/prepare.slt
b/datafusion/sqllogictest/test_files/prepare.slt
index 5d0f417640..33df0d26f3 100644
--- a/datafusion/sqllogictest/test_files/prepare.slt
+++ b/datafusion/sqllogictest/test_files/prepare.slt
@@ -312,3 +312,18 @@ SET datafusion.explain.logical_plan_only=false;
statement ok
DROP TABLE person;
+
+statement ok
+SET datafusion.explain.logical_plan_only=true;
+
+statement count 0
+PREPARE my_plan(STRING, STRING) AS SELECT * FROM (VALUES(1, $1), (2, $2)) AS t
(num, letter);
+
+statement count 5
+explain PREPARE my_plan(STRING, STRING) AS SELECT * FROM (VALUES(1, $1), (2,
$2)) AS t (num, letter);
+
+query IT
+EXECUTE my_plan('a', 'b');
+----
+1 a
+2 b
diff --git a/datafusion/sqllogictest/test_files/select.slt
b/datafusion/sqllogictest/test_files/select.slt
index f1ac0696bf..d5e0c44976 100644
--- a/datafusion/sqllogictest/test_files/select.slt
+++ b/datafusion/sqllogictest/test_files/select.slt
@@ -1820,6 +1820,9 @@ query I
select a from t;
----
+statement count 0
+drop table t;
+
statement ok
set datafusion.optimizer.max_passes=3;
@@ -1842,3 +1845,13 @@ SELECT t1.v1 FROM (SELECT 1 AS "t1.v1");
# Test issue: https://github.com/apache/datafusion/issues/14124
query error DataFusion error: Arrow error: Arithmetic overflow: Overflow
happened on: 10000 \* 100000000000000000000000000000000000
SELECT ('0.54321543215432154321543215432154321'::DECIMAL(35,35) +
10000)::VARCHAR
+
+# where_selection_with_ambiguous_column
+statement ok
+CREATE TABLE t(a int, b int, id int);
+
+query error DataFusion error: Schema error: Ambiguous reference to unqualified
field id
+select * from t a, t b where id = id + 1;
+
+statement count 0
+drop table t;
diff --git a/datafusion/sqllogictest/test_files/subquery.slt
b/datafusion/sqllogictest/test_files/subquery.slt
index 207bb72fd5..5a722c2288 100644
--- a/datafusion/sqllogictest/test_files/subquery.slt
+++ b/datafusion/sqllogictest/test_files/subquery.slt
@@ -1431,3 +1431,23 @@ drop table t1;
statement count 0
drop table t2;
+
+
+# test exists_subquery_wildcard
+statement count 0
+create table person(id int, last_name int, state int);
+
+query TT
+explain SELECT id FROM person p WHERE EXISTS
+ (SELECT * FROM person WHERE last_name = p.last_name AND state = p.state)
+----
+logical_plan
+01)Projection: p.id
+02)--LeftSemi Join: p.last_name = __correlated_sq_1.last_name, p.state =
__correlated_sq_1.state
+03)----SubqueryAlias: p
+04)------TableScan: person projection=[id, last_name, state]
+05)----SubqueryAlias: __correlated_sq_1
+06)------TableScan: person projection=[last_name, state]
+
+statement count 0
+drop table person;
diff --git a/datafusion/sqllogictest/test_files/wildcard.slt
b/datafusion/sqllogictest/test_files/wildcard.slt
index 7c076f040f..1a480eac0c 100644
--- a/datafusion/sqllogictest/test_files/wildcard.slt
+++ b/datafusion/sqllogictest/test_files/wildcard.slt
@@ -145,3 +145,18 @@ DROP TABLE t2;
statement ok
DROP TABLE aggregate_simple;
+
+statement ok
+create table t(a int, b int, c int) as values (1, 2, 3);
+
+query error DataFusion error: Error during planning: Projections require
unique expression names but the expression "t\.a" at position 0 and "t\.a" at
position 3 have the same name\. Consider aliasing \("AS"\) one of them\.
+select *, a from t;
+
+# a is aliased to other name so the query is valid
+query IIII
+select *, a as aka from t;
+----
+1 2 3 1
+
+statement count 0
+drop table t;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]