Skip to content

Commit 065e111

Browse files
Snowflake Dialect
1 parent 4cc97c8 commit 065e111

2 files changed

Lines changed: 54 additions & 35 deletions

File tree

datafusion/sql/src/unparser/plan.rs

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ impl Unparser<'_> {
318318
if is_unnest_col {
319319
return Ok(self.build_flatten_value_select_item(
320320
FLATTEN_DEFAULT_ALIAS,
321+
None,
321322
));
322323
}
323324
}
@@ -423,10 +424,14 @@ impl Unparser<'_> {
423424
// The projection generated by the `RecursiveUnnestRewriter` from a UNNEST relation will have
424425
// only one expression, which is the placeholder column generated by the rewriter.
425426
let unnest_input_type = if p.expr.len() == 1 {
426-
Self::check_unnest_placeholder_with_outer_ref(
427-
&p.expr[0],
428-
self.dialect.unnest_as_lateral_flatten(),
429-
)
427+
Self::check_unnest_placeholder_with_outer_ref(&p.expr[0])
428+
} else {
429+
None
430+
};
431+
// Extract the outermost user alias (e.g. "c1" from `UNNEST(...) AS c1`).
432+
// Internal aliases like "UNNEST(...)" are not user aliases.
433+
let user_alias = if unnest_input_type.is_some() {
434+
Self::extract_unnest_user_alias(&p.expr[0])
430435
} else {
431436
None
432437
};
@@ -466,8 +471,10 @@ impl Unparser<'_> {
466471
relation.flatten(flatten_relation);
467472

468473
if !select.already_projected() {
469-
let value_expr =
470-
self.build_flatten_value_select_item(&alias_name);
474+
let value_expr = self.build_flatten_value_select_item(
475+
&alias_name,
476+
user_alias.as_deref(),
477+
);
471478
select.projection(vec![value_expr]);
472479
}
473480

@@ -501,8 +508,10 @@ impl Unparser<'_> {
501508
flatten.outer(unnest.options.preserve_nulls);
502509

503510
if !select.already_projected() {
504-
let value_expr =
505-
self.build_flatten_value_select_item(&alias_name);
511+
let value_expr = self.build_flatten_value_select_item(
512+
&alias_name,
513+
user_alias.as_deref(),
514+
);
506515
select.projection(vec![value_expr]);
507516
}
508517

@@ -540,6 +549,7 @@ impl Unparser<'_> {
540549
}
541550
if self.dialect.unnest_as_table_factor()
542551
&& unnest_input_type.is_some()
552+
&& user_alias.is_none() // Skip if user alias present — fall through to reconstruct_select_statement which preserves aliases
543553
&& let LogicalPlan::Unnest(unnest) = &p.input.as_ref()
544554
&& let Some(unnest_relation) =
545555
self.try_unnest_to_table_factor_sql(unnest)?
@@ -1241,28 +1251,14 @@ impl Unparser<'_> {
12411251
/// - If the column is not a placeholder column, return [None].
12421252
///
12431253
/// `outer_ref` is the display result of [Expr::OuterReferenceColumn]
1244-
/// When `deep_peel` is true, peels through multiple Alias layers to find
1245-
/// the inner Column. This is needed for Snowflake FLATTEN where user aliases
1246-
/// (e.g. `AS items`) add extra Alias wrappers around the placeholder column.
1247-
fn check_unnest_placeholder_with_outer_ref(
1248-
expr: &Expr,
1249-
deep_peel: bool,
1250-
) -> Option<UnnestInputType> {
1251-
let inner = match expr {
1252-
Expr::Alias(Alias { expr, .. }) => {
1253-
if deep_peel {
1254-
// Peel through all Alias layers
1255-
let mut e = expr.as_ref();
1256-
while let Expr::Alias(Alias { expr: next, .. }) = e {
1257-
e = next.as_ref();
1258-
}
1259-
e
1260-
} else {
1261-
expr.as_ref()
1262-
}
1263-
}
1264-
_ => return None,
1265-
};
1254+
fn check_unnest_placeholder_with_outer_ref(expr: &Expr) -> Option<UnnestInputType> {
1255+
// Peel through all Alias layers to find the inner Column.
1256+
// The expression may have multiple aliases, e.g.:
1257+
// Alias("items", Alias("UNNEST(...)", Column("__unnest_placeholder(...)")))
1258+
let mut inner = expr;
1259+
while let Expr::Alias(Alias { expr, .. }) = inner {
1260+
inner = expr.as_ref();
1261+
}
12661262
if let Expr::Column(Column { name, .. }) = inner
12671263
&& let Some(prefix) = name.strip_prefix(UNNEST_PLACEHOLDER)
12681264
{
@@ -1274,6 +1270,19 @@ impl Unparser<'_> {
12741270
None
12751271
}
12761272

1273+
/// Extract the outermost user-provided alias from an unnest expression.
1274+
/// Returns `None` if the outermost alias is DataFusion's internal display
1275+
/// name (e.g. `UNNEST(make_array(...))`), or if there is no alias at all.
1276+
fn extract_unnest_user_alias(expr: &Expr) -> Option<String> {
1277+
if let Expr::Alias(Alias { name, .. }) = expr {
1278+
// Internal aliases start with "UNNEST(" — user aliases don't.
1279+
if !name.starts_with(&format!("{UNNEST_COLUMN_PREFIX}(")) {
1280+
return Some(name.clone());
1281+
}
1282+
}
1283+
None
1284+
}
1285+
12771286
fn try_unnest_to_table_factor_sql(
12781287
&self,
12791288
unnest: &Unnest,
@@ -1305,12 +1314,22 @@ impl Unparser<'_> {
13051314
}
13061315

13071316
/// Build a `SELECT alias."VALUE"` item for Snowflake FLATTEN output.
1308-
fn build_flatten_value_select_item(&self, alias_name: &str) -> ast::SelectItem {
1317+
fn build_flatten_value_select_item(
1318+
&self,
1319+
flatten_alias: &str,
1320+
user_alias: Option<&str>,
1321+
) -> ast::SelectItem {
13091322
let compound = ast::Expr::CompoundIdentifier(vec![
1310-
self.new_ident_without_quote_style(alias_name.to_string()),
1323+
self.new_ident_without_quote_style(flatten_alias.to_string()),
13111324
Ident::with_quote('"', "VALUE"),
13121325
]);
1313-
ast::SelectItem::UnnamedExpr(compound)
1326+
match user_alias {
1327+
Some(alias) => ast::SelectItem::ExprWithAlias {
1328+
expr: compound,
1329+
alias: self.new_ident_quoted_if_needs(alias.to_string()),
1330+
},
1331+
None => ast::SelectItem::UnnamedExpr(compound),
1332+
}
13141333
}
13151334

13161335
/// Convert an `Unnest` logical plan node to a `LATERAL FLATTEN(INPUT => expr, ...)`

datafusion/sql/tests/cases/plan_to_sql.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3073,7 +3073,7 @@ fn snowflake_flatten_select_unnest_with_alias() -> Result<(), DataFusionError> {
30733073
sql: "SELECT UNNEST([1,2,3]) as c1",
30743074
parser_dialect: GenericDialect {},
30753075
unparser_dialect: snowflake,
3076-
expected: @r#"SELECT _unnest."VALUE" FROM LATERAL FLATTEN(INPUT => [1, 2, 3]) AS _unnest"#,
3076+
expected: @r#"SELECT _unnest."VALUE" AS "c1" FROM LATERAL FLATTEN(INPUT => [1, 2, 3]) AS _unnest"#,
30773077
);
30783078
Ok(())
30793079
}
@@ -3180,7 +3180,7 @@ fn snowflake_flatten_unnest_udf_result() -> Result<(), DataFusionError> {
31803180
let result = unparser.plan_to_sql(&plan)?;
31813181
let actual = result.to_string();
31823182

3183-
insta::assert_snapshot!(actual, @r#"SELECT _unnest."VALUE" FROM "j1" CROSS JOIN LATERAL FLATTEN(INPUT => json_get_array("j1"."j1_string")) AS _unnest LIMIT 5"#);
3183+
insta::assert_snapshot!(actual, @r#"SELECT _unnest."VALUE" AS "items" FROM "j1" CROSS JOIN LATERAL FLATTEN(INPUT => json_get_array("j1"."j1_string")) AS _unnest LIMIT 5"#);
31843184
Ok(())
31853185
}
31863186

0 commit comments

Comments
 (0)