Skip to content

Commit

Permalink
fix(python,rust,cli): preserve expression aliases when parsing SQL wi…
Browse files Browse the repository at this point in the history
…th `pl.sql_expr` (#9875)
  • Loading branch information
alexander-beedie authored Jul 14, 2023
1 parent 5937c22 commit e142383
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 8 deletions.
3 changes: 2 additions & 1 deletion polars/polars-sql/src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ impl PolarsSqlFunctions {
"min",
"octet_length",
"pow",
"power",
"radians",
"round",
"rtrim",
Expand Down Expand Up @@ -432,7 +433,7 @@ impl TryFrom<&'_ SQLFunction> for PolarsSqlFunctions {
"log10" => Self::Log10,
"log1p" => Self::Log1p,
"log2" => Self::Log2,
"pow" => Self::Pow,
"pow" | "power" => Self::Pow,
"round" => Self::Round,

// ----
Expand Down
16 changes: 11 additions & 5 deletions polars/polars-sql/src/sql_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use polars_lazy::prelude::*;
use polars_plan::prelude::{col, lit, when};
use sqlparser::ast::{
ArrayAgg, BinaryOperator as SQLBinaryOperator, BinaryOperator, DataType as SQLDataType,
Expr as SqlExpr, Function as SQLFunction, JoinConstraint, OrderByExpr, TrimWhereField,
UnaryOperator, Value as SqlValue,
Expr as SqlExpr, Function as SQLFunction, JoinConstraint, OrderByExpr, SelectItem,
TrimWhereField, UnaryOperator, Value as SqlValue,
};
use sqlparser::dialect::GenericDialect;
use sqlparser::parser::{Parser, ParserOptions};
Expand Down Expand Up @@ -532,8 +532,14 @@ pub fn sql_expr<S: AsRef<str>>(s: S) -> PolarsResult<Expr> {
});

let mut ast = parser.try_with_sql(s.as_ref()).map_err(to_compute_err)?;
let expr = ast.parse_select_item().map_err(to_compute_err)?;

let expr = ast.parse_expr().map_err(to_compute_err)?;

parse_sql_expr(&expr, &ctx)
Ok(match &expr {
SelectItem::ExprWithAlias { expr, alias } => {
let expr = parse_sql_expr(expr, &ctx)?;
expr.alias(&alias.value)
}
SelectItem::UnnamedExpr(expr) => parse_sql_expr(expr, &ctx)?,
_ => polars_bail!(InvalidOperation: "Unable to parse '{}' as Expr", s.as_ref()),
})
}
14 changes: 12 additions & 2 deletions py-polars/tests/unit/test_sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,17 @@ def test_sql_expr() -> None:
df = pl.DataFrame({"a": [1, 2, 3], "b": ["xyz", "abcde", None]})
sql_exprs = (
pl.sql_expr("MIN(a)"),
pl.sql_expr("SUBSTR(b,1,2)"),
pl.sql_expr("POWER(a,a) AS aa"),
pl.sql_expr("SUBSTR(b,1,2) AS b2"),
)
expected = pl.DataFrame(
{"a": [1, 1, 1], "aa": [1, 4, 27], "b2": ["yz", "bc", None]}
)
expected = pl.DataFrame({"a": [1, 1, 1], "b": ["yz", "bc", None]})
assert df.select(sql_exprs).frame_equal(expected)

# expect expressions that can't reasonably be parsed as expressions to raise
# (for example: those that explicitly reference tables and/or use wildcards)
with pytest.raises(
pl.InvalidOperationError, match=r"Unable to parse 'xyz\.\*' as Expr"
):
pl.sql_expr("xyz.*")

0 comments on commit e142383

Please sign in to comment.