Skip to content

Commit

Permalink
feat: Support Substring(str [from int] [for int]) (#1621)
Browse files Browse the repository at this point in the history
  • Loading branch information
ovr authored Jan 24, 2022
1 parent 97f95b3 commit 618c1e8
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 4 deletions.
2 changes: 1 addition & 1 deletion datafusion/src/physical_plan/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3714,7 +3714,7 @@ mod tests {
lit(ScalarValue::Int64(Some(-1))),
],
Err(DataFusionError::Execution(
"negative substring length not allowed".to_string(),
"negative substring length not allowed: substr(<str>, 1, -1)".to_string(),
)),
&str,
Utf8,
Expand Down
8 changes: 5 additions & 3 deletions datafusion/src/physical_plan/unicode_expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,9 +452,11 @@ pub fn substr<T: StringOffsetSizeTrait>(args: &[ArrayRef]) -> Result<ArrayRef> {
.map(|((string, start), count)| match (string, start, count) {
(Some(string), Some(start), Some(count)) => {
if count < 0 {
Err(DataFusionError::Execution(
"negative substring length not allowed".to_string(),
))
Err(DataFusionError::Execution(format!(
"negative substring length not allowed: substr(<str>, {}, {})",
start,
count
)))
} else if start <= 0 {
Ok(Some(string.to_string()))
} else {
Expand Down
48 changes: 48 additions & 0 deletions datafusion/src/sql/planner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1542,6 +1542,54 @@ impl<'a, S: ContextProvider> SqlToRel<'a, S> {
ref right,
} => self.parse_sql_binary_op(left, op, right, schema),

SQLExpr::Substring {
expr,
substring_from,
substring_for,
} => {
#[cfg(feature = "unicode_expressions")]
{
let arg = self.sql_expr_to_logical_expr(expr, schema)?;
let args = match (substring_from, substring_for) {
(Some(from_expr), Some(for_expr)) => {
let from_logic =
self.sql_expr_to_logical_expr(from_expr, schema)?;
let for_logic =
self.sql_expr_to_logical_expr(for_expr, schema)?;
vec![arg, from_logic, for_logic]
}
(Some(from_expr), None) => {
let from_logic =
self.sql_expr_to_logical_expr(from_expr, schema)?;
vec![arg, from_logic]
}
(None, Some(for_expr)) => {
let from_logic = Expr::Literal(ScalarValue::Int64(Some(1)));
let for_logic =
self.sql_expr_to_logical_expr(for_expr, schema)?;
vec![arg, from_logic, for_logic]
}
_ => {
return Err(DataFusionError::Plan(format!(
"Substring without for/from is not valid {:?}",
sql
)))
}
};
Ok(Expr::ScalarFunction {
fun: functions::BuiltinScalarFunction::Substr,
args,
})
}

#[cfg(not(feature = "unicode_expressions"))]
{
Err(DataFusionError::Internal(
"statement substring requires compilation with feature flag: unicode_expressions.".to_string()
))
}
}

SQLExpr::Trim { expr, trim_where } => {
let (fun, where_expr) = match trim_where {
Some((TrimWhereField::Leading, expr)) => {
Expand Down
10 changes: 10 additions & 0 deletions datafusion/tests/sql/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,16 @@ async fn test_interval_expressions() -> Result<()> {
Ok(())
}

#[cfg(feature = "unicode_expressions")]
#[tokio::test]
async fn test_substring_expr() -> Result<()> {
test_expression!("substring('alphabet' from 2 for 1)", "l");
test_expression!("substring('alphabet' from 8)", "t");
test_expression!("substring('alphabet' for 1)", "a");

Ok(())
}

#[tokio::test]
async fn test_string_expressions() -> Result<()> {
test_expression!("ascii('')", "0");
Expand Down

0 comments on commit 618c1e8

Please sign in to comment.