Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implemented substring function #186

Merged
merged 2 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## Added
- `SUBSTR` function [MR186](https://github.com/Qrlew/qrlew/pull/186)

## [0.4.13] - 2023-11-14
## Added
Expand Down
131 changes: 128 additions & 3 deletions src/data_type/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,46 @@ impl Pointwise {
}),
)
}

/// Build trivariate pointwise function
pub fn trivariate<A: Variant, B: Variant, C: Variant, D: Variant>(
domain: (A, B, C),
co_domain: D,
value: impl Fn(
<A::Element as value::Variant>::Wrapped,
<B::Element as value::Variant>::Wrapped,
<C::Element as value::Variant>::Wrapped
) -> <D::Element as value::Variant>::Wrapped
+ Sync
+ Send
+ 'static,
) -> Self
where
<A::Element as value::Variant>::Wrapped: TryFrom<Value>,
<B::Element as value::Variant>::Wrapped: TryFrom<Value>,
<C::Element as value::Variant>::Wrapped: TryFrom<Value>,
<<A::Element as value::Variant>::Wrapped as TryFrom<Value>>::Error: fmt::Debug,
Error: From<<<A::Element as value::Variant>::Wrapped as TryFrom<Value>>::Error>,
<<B::Element as value::Variant>::Wrapped as TryFrom<Value>>::Error: fmt::Debug,
Error: From<<<B::Element as value::Variant>::Wrapped as TryFrom<Value>>::Error>,
<<C::Element as value::Variant>::Wrapped as TryFrom<Value>>::Error: fmt::Debug,
Error: From<<<C::Element as value::Variant>::Wrapped as TryFrom<Value>>::Error>,
<D::Element as value::Variant>::Wrapped: Into<Value>,
{
let domain = data_type::Struct::from_data_types(&[domain.0.into(), domain.1.into(), domain.2.into()]);
Self::new(
domain.into(),
co_domain.into(),
Arc::new(move |ab| {
let abc = value::Struct::try_from(ab).unwrap();
let a = <A::Element as value::Variant>::Wrapped::try_from(abc[0].as_ref().clone());
let b = <B::Element as value::Variant>::Wrapped::try_from(abc[1].as_ref().clone());
let c = <C::Element as value::Variant>::Wrapped::try_from(abc[2].as_ref().clone());
Ok(a.map(|a| b.map(|b| c.map( |c| value(a, b, c).into())))???)
}),
)
}

/// Build variadic pointwise function
pub fn variadic<D: Variant, C: Variant>(
domain: Vec<D>,
Expand Down Expand Up @@ -1296,10 +1336,37 @@ pub fn ltrim() -> impl Function {
)
}

pub fn substr() -> impl Function {
Pointwise::bivariate(
(data_type::Text::default(), data_type::Integer::default()),
data_type::Text::default(),
|a, b| {
let start = b as usize;
a.as_str().get(start..).unwrap_or("").to_string()
}
)
}

pub fn substr_with_size() -> impl Function {
Pointwise::trivariate(
(data_type::Text::default(), data_type::Integer::default(), data_type::Integer::default()),
data_type::Text::default(),
|a, b, c| {
let start = b as usize;
let end = cmp::min((b + c) as usize, a.len());
a.as_str().get(start..end).unwrap_or("").to_string()
}
)
}

pub fn concat(n: usize) -> impl Function {
Pointwise::variadic(vec![DataType::Any; n], data_type::Text::default(), |v| {
v.into_iter().map(|v| v.to_string()).join("")
})
Pointwise::variadic(
vec![DataType::Any; n],
data_type::Text::default(),
|v| {
v.into_iter().map(|v| v.to_string()).join("")
}
)
}

pub fn md5() -> impl Function {
Expand Down Expand Up @@ -3117,4 +3184,62 @@ mod tests {
println!("val({}) = {}", arg, val);
assert_eq!(val, Value::from("arus".to_string()));
}

#[test]
fn test_substr() {
println!("Test substr");
let fun = substr();
println!("type = {}", fun);
println!("domain = {}", fun.domain());
println!("co_domain = {}", fun.co_domain());

let set = DataType::from(Struct::from_data_types(&[
DataType::text(),
DataType::integer(),
]));
let im = fun.super_image(&set).unwrap();
println!("im({}) = {}", set, im);
assert!(im == DataType::text());

let set = DataType::from(Struct::from_data_types(&[
DataType::text_values(["abcdefg".to_string(), "hijklmno".to_string()]),
DataType::integer_values([3, 6, 10]),
]));
let im = fun.super_image(&set).unwrap();
println!("im({}) = {}", set, im);
assert_eq!(
im,
DataType::text_values(["".to_string(), "defg".to_string(), "g".to_string(), "klmno".to_string(), "no".to_string()])
);
}

#[test]
fn test_substr_with_size() {
println!("Test substr_with_size");
let fun = substr_with_size();
println!("type = {}", fun);
println!("domain = {}", fun.domain());
println!("co_domain = {}", fun.co_domain());

let set = DataType::from(Struct::from_data_types(&[
DataType::text(),
DataType::integer(),
DataType::integer()
]));
let im = fun.super_image(&set).unwrap();
println!("im({}) = {}", set, im);
assert!(im == DataType::text());

let set = DataType::from(Struct::from_data_types(&[
DataType::text_values(["abcdefg".to_string(), "hijklmno".to_string()]),
DataType::integer_values([3, 6, 10]),
DataType::integer_value(2)
]));
let im = fun.super_image(&set).unwrap();
println!("im({}) = {}", set, im);
assert_eq!(
im,
DataType::text_values(["".to_string(), "de".to_string(), "g".to_string(), "kl".to_string(), "no".to_string()])
);
}
}
11 changes: 9 additions & 2 deletions src/expr/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ pub enum Function {
Greatest,
Rtrim,
Ltrim,
Substr,
SubstrWithSize,
}

#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
Expand Down Expand Up @@ -126,8 +128,10 @@ impl Function {
| Function::Coalesce
| Function::Rtrim
| Function::Ltrim
| Function::Substr
// Ternary Function
| Function::Case
| Function::SubstrWithSize
// Nary Function
| Function::Concat(_) => Style::Function,
}
Expand Down Expand Up @@ -183,11 +187,12 @@ impl Function {
| Function::Greatest
| Function::Coalesce
| Function::Rtrim
| Function::Ltrim => {
| Function::Ltrim
| Function::Substr => {
Arity::Nary(2)
}
// Ternary Function
Function::Case => Arity::Nary(3),
Function::Case | Function::SubstrWithSize => Arity::Nary(3),
// Nary Function
Function::Concat(_) => Arity::Varying,
}
Expand Down Expand Up @@ -266,8 +271,10 @@ impl fmt::Display for Function {
Function::Coalesce => "coalesce",
Function::Rtrim => "rtrim",
Function::Ltrim => "ltrim",
Function::Substr => "substr",
// Ternary Functions
Function::Case => "case",
Function::SubstrWithSize => "substr",
// Nary Functions
Function::Concat(_) => "concat",
})
Expand Down
5 changes: 3 additions & 2 deletions src/expr/implementation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,10 @@ function_implementations!(
Least,
Greatest,
Rtrim,
Ltrim
Ltrim,
Substr
],
[Case, Position],
[Case, Position, SubstrWithSize],
x,
{
match x {
Expand Down
48 changes: 46 additions & 2 deletions src/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,8 @@ impl_binary_function_constructors!(
Greatest,
Coalesce,
Rtrim,
Ltrim
Ltrim,
Substr
);

/// Implement ternary function constructors
Expand Down Expand Up @@ -363,7 +364,7 @@ macro_rules! impl_ternary_function_constructors {
};
}

impl_ternary_function_constructors!(Case);
impl_ternary_function_constructors!(Case, SubstrWithSize);

/// Implement nary function constructors
macro_rules! impl_nary_function_constructors {
Expand Down Expand Up @@ -2806,4 +2807,47 @@ mod tests {
DataType::text_values(["".to_string(), "ab".to_string(), "abb".to_string(), "bb".to_string(), "bbb".to_string()])
);
}

#[test]
fn test_substr() {
let expression = Expr::substr(
Expr::col("col1".to_string()),
Expr::val("2".to_string()),
);
println!("\nexpression = {}", expression);
println!("expression data type = {}", expression.data_type());
let set = DataType::structured([
("col1", DataType::optional(DataType::text_values(["abcdefg".to_string(), "hijkl".to_string()]))),
]);
println!(
"expression super image = {}",
expression.super_image(&set).unwrap()
);
assert_eq!(
expression.super_image(&set).unwrap(),
DataType::optional(DataType::text())
);
}

#[test]
fn test_substr_with_size() {
let expression = Expr::substr_with_size(
Expr::col("col1".to_string()),
Expr::val("2".to_string()),
Expr::val("4".to_string()),
);
println!("\nexpression = {}", expression);
println!("expression data type = {}", expression.data_type());
let set = DataType::structured([
("col1", DataType::text_values(["abcdefg".to_string(), "hijkl".to_string()])),
]);
println!(
"expression super image = {}",
expression.super_image(&set).unwrap()
);
assert_eq!(
expression.super_image(&set).unwrap(),
DataType::optional(DataType::text())
);
}
}
49 changes: 48 additions & 1 deletion src/expr/sql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,9 @@ impl<'a> expr::Visitor<'a, ast::Expr> for FromExprVisitor {
| expr::function::Function::Greatest
| expr::function::Function::Coalesce
| expr::function::Function::Rtrim
| expr::function::Function::Ltrim => ast::Expr::Function(ast::Function {
| expr::function::Function::Ltrim
| expr::function::Function::Substr
| expr::function::Function::SubstrWithSize => ast::Expr::Function(ast::Function {
name: ast::ObjectName(vec![ast::Ident::new(function.to_string())]),
args: arguments
.into_iter()
Expand Down Expand Up @@ -483,4 +485,49 @@ mod tests {
println!("ast::expr = {:?}", ast_expr);
assert_eq!(ast_expr.to_string(), str_expr.to_string(),);
}

#[test]
fn test_substr() {
let str_expr = "substr(a, 5, 2)";
let ast_expr: ast::Expr = parse_expr(str_expr).unwrap();
let expr = Expr::try_from(&ast_expr).unwrap();
println!("expr = {}", expr);
let gen_expr = ast::Expr::from(&expr);
println!("ast::expr = {gen_expr}");
assert_eq!(ast_expr, gen_expr);

let str_expr = "\nsubstr(a, 5)";
let ast_expr: ast::Expr = parse_expr(str_expr).unwrap();
let expr = Expr::try_from(&ast_expr).unwrap();
println!("expr = {}", expr);
let gen_expr = ast::Expr::from(&expr);
println!("ast::expr = {gen_expr}");
assert_eq!(ast_expr, gen_expr);

let str_expr = "\nsubstring(a from 5 for 2)";
let ast_expr: ast::Expr = parse_expr(str_expr).unwrap();
let expr = Expr::try_from(&ast_expr).unwrap();
println!("expr = {}", expr);
let gen_expr = ast::Expr::from(&expr);
println!("ast::expr = {gen_expr}");
assert_eq!(gen_expr, parse_expr("substr(a, 5, 2)").unwrap());

let str_expr = "\nsubstring(a from 5)";
let ast_expr: ast::Expr = parse_expr(str_expr).unwrap();
let expr = Expr::try_from(&ast_expr).unwrap();
println!("expr = {}", expr);
let gen_expr = ast::Expr::from(&expr);
println!("ast::expr = {gen_expr}");
assert_eq!(gen_expr, parse_expr("substr(a, 5)").unwrap());

let str_expr = "\nsubstring(a for 5)";
let ast_expr: ast::Expr = parse_expr(str_expr).unwrap();
let expr = Expr::try_from(&ast_expr).unwrap();
println!("expr = {}", expr);
let gen_expr = ast::Expr::from(&expr);
println!("ast::expr = {gen_expr}");
assert_eq!(gen_expr, parse_expr("substr(a, 0, 5)").unwrap());
}


}
Loading